diff options
Diffstat (limited to 'source/blender/editors/curve')
-rw-r--r-- | source/blender/editors/curve/Makefile | 56 | ||||
-rw-r--r-- | source/blender/editors/curve/SConscript | 11 | ||||
-rw-r--r-- | source/blender/editors/curve/curve_intern.h | 109 | ||||
-rw-r--r-- | source/blender/editors/curve/curve_ops.c | 245 | ||||
-rw-r--r-- | source/blender/editors/curve/editcurve.c | 5235 | ||||
-rw-r--r-- | source/blender/editors/curve/editfont.c | 1625 | ||||
-rw-r--r-- | source/blender/editors/curve/lorem.c | 513 |
7 files changed, 7794 insertions, 0 deletions
diff --git a/source/blender/editors/curve/Makefile b/source/blender/editors/curve/Makefile new file mode 100644 index 00000000000..6b1f628f231 --- /dev/null +++ b/source/blender/editors/curve/Makefile @@ -0,0 +1,56 @@ +# +# $Id: Makefile 14 2002-10-13 15:57:19Z hans $ +# +# ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# The Original Code is Copyright (C) 2007 Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): none yet. +# +# ***** END GPL LICENSE BLOCK ***** +# +# Makes module object directory and bounces make to subdirectories. + +LIBNAME = ed_curve +DIR = $(OCGDIR)/blender/$(LIBNAME) + +include nan_compile.mk + +CFLAGS += $(LEVEL_1_C_WARNINGS) + +CPPFLAGS += -I$(NAN_GLEW)/include +CPPFLAGS += -I$(OPENGL_HEADERS) + +CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include +CPPFLAGS += -I$(NAN_ELBEEM)/include + +CPPFLAGS += -I../../windowmanager +CPPFLAGS += -I../../blenkernel +CPPFLAGS += -I../../blenloader +CPPFLAGS += -I../../blenlib +CPPFLAGS += -I../../makesdna +CPPFLAGS += -I../../makesrna +CPPFLAGS += -I../../imbuf +CPPFLAGS += -I../../gpu +CPPFLAGS += -I../../render/extern/include + +# own include + +CPPFLAGS += -I../include diff --git a/source/blender/editors/curve/SConscript b/source/blender/editors/curve/SConscript new file mode 100644 index 00000000000..3a1930899d3 --- /dev/null +++ b/source/blender/editors/curve/SConscript @@ -0,0 +1,11 @@ +#!/usr/bin/python +Import ('env') + +sources = env.Glob('*.c') + +incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' +incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' +incs += ' #/intern/guardedalloc ../../gpu' +incs += ' ../../makesrna ../../render/extern/include #/intern/elbeem/extern' + +env.BlenderLib ( 'bf_editors_curve', sources, Split(incs), [], libtype=['core'], priority=[45] ) diff --git a/source/blender/editors/curve/curve_intern.h b/source/blender/editors/curve/curve_intern.h new file mode 100644 index 00000000000..a73a54323ee --- /dev/null +++ b/source/blender/editors/curve/curve_intern.h @@ -0,0 +1,109 @@ +/** + * $Id: + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef ED_CURVE_INTERN_H +#define ED_CURVE_INTERN_H + +/* internal exports only */ +struct wmOperatorType; + +/* lorem.c */ +char *ED_lorem; + +/* editfont.c */ +enum { DEL_ALL, DEL_NEXT_CHAR, DEL_PREV_CHAR, DEL_SELECTION, DEL_NEXT_SEL, DEL_PREV_SEL }; +enum { CASE_LOWER, CASE_UPPER }; +enum { LINE_BEGIN, LINE_END, PREV_CHAR, NEXT_CHAR, PREV_WORD, NEXT_WORD, + PREV_LINE, NEXT_LINE, PREV_PAGE, NEXT_PAGE }; + +void FONT_OT_text_insert(struct wmOperatorType *ot); +void FONT_OT_line_break(struct wmOperatorType *ot); +void FONT_OT_insert_lorem(struct wmOperatorType *ot); + +void FONT_OT_case_toggle(struct wmOperatorType *ot); +void FONT_OT_case_set(struct wmOperatorType *ot); +void FONT_OT_style_toggle(struct wmOperatorType *ot); +void FONT_OT_style_set(struct wmOperatorType *ot); +void FONT_OT_material_set(struct wmOperatorType *ot); + +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_file_paste(struct wmOperatorType *ot); +void FONT_OT_buffer_paste(struct wmOperatorType *ot); + +void FONT_OT_move(struct wmOperatorType *ot); +void FONT_OT_move_select(struct wmOperatorType *ot); +void FONT_OT_delete(struct wmOperatorType *ot); + +void FONT_OT_change_character(struct wmOperatorType *ot); +void FONT_OT_change_spacing(struct wmOperatorType *ot); + +/* editcurve.c */ +void CURVE_OT_hide(struct wmOperatorType *ot); +void CURVE_OT_reveal(struct wmOperatorType *ot); + +void CURVE_OT_separate(struct wmOperatorType *ot); +void CURVE_OT_duplicate(struct wmOperatorType *ot); +void CURVE_OT_delete(struct wmOperatorType *ot); + +void CURVE_OT_spline_type_set(struct wmOperatorType *ot); +void CURVE_OT_radius_set(struct wmOperatorType *ot); +void CURVE_OT_spline_weight_set(struct wmOperatorType *ot); +void CURVE_OT_handle_type_set(struct wmOperatorType *ot); +void CURVE_OT_smooth_set(struct wmOperatorType *ot); +void CURVE_OT_tilt_clear(struct wmOperatorType *ot); + +void CURVE_OT_smooth(struct wmOperatorType *ot); +void CURVE_OT_smooth_radius(struct wmOperatorType *ot); + +void CURVE_OT_de_select_first(struct wmOperatorType *ot); +void CURVE_OT_de_select_last(struct wmOperatorType *ot); +void CURVE_OT_select_all_toggle(struct wmOperatorType *ot); +void CURVE_OT_select_invert(struct wmOperatorType *ot); +void CURVE_OT_select_linked(struct wmOperatorType *ot); +void CURVE_OT_select_row(struct wmOperatorType *ot); +void CURVE_OT_select_next(struct wmOperatorType *ot); +void CURVE_OT_select_previous(struct wmOperatorType *ot); +void CURVE_OT_select_more(struct wmOperatorType *ot); +void CURVE_OT_select_less(struct wmOperatorType *ot); +void CURVE_OT_select_random(struct wmOperatorType *ot); +void CURVE_OT_select_every_nth(struct wmOperatorType *ot); + +void CURVE_OT_switch_direction(struct wmOperatorType *ot); +void CURVE_OT_subdivide(struct wmOperatorType *ot); +void CURVE_OT_make_segment(struct wmOperatorType *ot); +void CURVE_OT_spin(struct wmOperatorType *ot); +void CURVE_OT_vertex_add(struct wmOperatorType *ot); +void CURVE_OT_extrude(struct wmOperatorType *ot); +void CURVE_OT_cyclic_toggle(struct wmOperatorType *ot); + +void CURVE_OT_specials_menu(struct wmOperatorType *ot); + +#endif /* ED_UTIL_INTERN_H */ + diff --git a/source/blender/editors/curve/curve_ops.c b/source/blender/editors/curve/curve_ops.c new file mode 100644 index 00000000000..5292d86d3c9 --- /dev/null +++ b/source/blender/editors/curve/curve_ops.c @@ -0,0 +1,245 @@ +/** + * $Id: + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2009 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <stdlib.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_curve_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_userdef_types.h" +#include "DNA_view3d_types.h" +#include "DNA_windowmanager_types.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_utildefines.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_screen.h" +#include "ED_object.h" + +#include "BIF_transform.h" + +#include "UI_interface.h" + +#include "curve_intern.h" + + +/**************************** menus *****************************/ + +static int specials_menu_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + uiPopupMenu *pup; + uiLayout *layout; + + pup= uiPupMenuBegin(C, "Specials", 0); + layout= uiPupMenuLayout(pup); + uiItemO(layout, NULL, 0, "CURVE_OT_subdivide"); + uiItemO(layout, NULL, 0, "CURVE_OT_switch_direction"); + uiItemO(layout, NULL, 0, "CURVE_OT_spline_weight_set"); + uiItemO(layout, NULL, 0, "CURVE_OT_radius_set"); + uiItemO(layout, NULL, 0, "CURVE_OT_smooth"); + uiItemO(layout, NULL, 0, "CURVE_OT_smooth_radius"); + uiPupMenuEnd(C, pup); + + return OPERATOR_CANCELLED; +} + +void CURVE_OT_specials_menu(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Specials Menu"; + ot->idname= "CURVE_OT_specials_menu"; + + /* api clastbacks */ + ot->invoke= specials_menu_invoke; + ot->poll= ED_operator_editsurfcurve; +} + +/************************* registration ****************************/ + +void ED_operatortypes_curve(void) +{ + WM_operatortype_append(FONT_OT_text_insert); + WM_operatortype_append(FONT_OT_line_break); + WM_operatortype_append(FONT_OT_insert_lorem); + + WM_operatortype_append(FONT_OT_case_toggle); + WM_operatortype_append(FONT_OT_case_set); + WM_operatortype_append(FONT_OT_style_toggle); + WM_operatortype_append(FONT_OT_style_set); + WM_operatortype_append(FONT_OT_material_set); + + WM_operatortype_append(FONT_OT_text_copy); + WM_operatortype_append(FONT_OT_text_cut); + WM_operatortype_append(FONT_OT_text_paste); + WM_operatortype_append(FONT_OT_file_paste); + WM_operatortype_append(FONT_OT_buffer_paste); + + WM_operatortype_append(FONT_OT_move); + WM_operatortype_append(FONT_OT_move_select); + WM_operatortype_append(FONT_OT_delete); + + WM_operatortype_append(FONT_OT_change_character); + WM_operatortype_append(FONT_OT_change_spacing); + + WM_operatortype_append(CURVE_OT_hide); + WM_operatortype_append(CURVE_OT_reveal); + + WM_operatortype_append(CURVE_OT_separate); + WM_operatortype_append(CURVE_OT_duplicate); + WM_operatortype_append(CURVE_OT_delete); + + WM_operatortype_append(CURVE_OT_spline_type_set); + WM_operatortype_append(CURVE_OT_radius_set); + WM_operatortype_append(CURVE_OT_spline_weight_set); + WM_operatortype_append(CURVE_OT_handle_type_set); + WM_operatortype_append(CURVE_OT_smooth_set); + WM_operatortype_append(CURVE_OT_tilt_clear); + + WM_operatortype_append(CURVE_OT_smooth); + WM_operatortype_append(CURVE_OT_smooth_radius); + + WM_operatortype_append(CURVE_OT_de_select_first); + WM_operatortype_append(CURVE_OT_de_select_last); + WM_operatortype_append(CURVE_OT_select_all_toggle); + WM_operatortype_append(CURVE_OT_select_invert); + WM_operatortype_append(CURVE_OT_select_linked); + WM_operatortype_append(CURVE_OT_select_row); + WM_operatortype_append(CURVE_OT_select_next); + WM_operatortype_append(CURVE_OT_select_previous); + WM_operatortype_append(CURVE_OT_select_more); + WM_operatortype_append(CURVE_OT_select_less); + WM_operatortype_append(CURVE_OT_select_random); + WM_operatortype_append(CURVE_OT_select_every_nth); + + WM_operatortype_append(CURVE_OT_switch_direction); + WM_operatortype_append(CURVE_OT_subdivide); + 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_extrude); + WM_operatortype_append(CURVE_OT_cyclic_toggle); + + WM_operatortype_append(CURVE_OT_specials_menu); +} + +void ED_keymap_curve(wmWindowManager *wm) +{ + ListBase *keymap= WM_keymap_listbase(wm, "Font", 0, 0); + + /* only set in editmode font, by space_view3d listener */ + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_style_toggle", BKEY, KM_PRESS, KM_CTRL, 0)->ptr, "style", CU_BOLD); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_style_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "style", CU_ITALIC); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_style_toggle", UKEY, KM_PRESS, KM_CTRL, 0)->ptr, "style", CU_UNDERLINE); + + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_delete", DELKEY, KM_PRESS, 0, 0)->ptr, "type", DEL_NEXT_SEL); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_delete", BACKSPACEKEY, KM_PRESS, 0, 0)->ptr, "type", DEL_PREV_SEL); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_delete", BACKSPACEKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", DEL_ALL); + + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", HOMEKEY, KM_PRESS, 0, 0)->ptr, "type", LINE_BEGIN); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", ENDKEY, KM_PRESS, 0, 0)->ptr, "type", LINE_END); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", LEFTARROWKEY, KM_PRESS, 0, 0)->ptr, "type", PREV_CHAR); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", RIGHTARROWKEY, KM_PRESS, 0, 0)->ptr, "type", NEXT_CHAR); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", LEFTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", PREV_WORD); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", RIGHTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", NEXT_WORD); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", UPARROWKEY, KM_PRESS, 0, 0)->ptr, "type", PREV_LINE); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", DOWNARROWKEY, KM_PRESS, 0, 0)->ptr, "type", NEXT_LINE); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", PAGEUPKEY, KM_PRESS, 0, 0)->ptr, "type", PREV_PAGE); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", PAGEDOWNKEY, KM_PRESS, 0, 0)->ptr, "type", NEXT_PAGE); + + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", HOMEKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", LINE_BEGIN); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", ENDKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", LINE_END); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", LEFTARROWKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", PREV_CHAR); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", RIGHTARROWKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", NEXT_CHAR); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", LEFTARROWKEY, KM_PRESS, KM_SHIFT|KM_CTRL, 0)->ptr, "type", PREV_WORD); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", RIGHTARROWKEY, KM_PRESS, KM_SHIFT|KM_CTRL, 0)->ptr, "type", NEXT_WORD); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", UPARROWKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", PREV_LINE); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", DOWNARROWKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", NEXT_LINE); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", PREV_PAGE); + RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", NEXT_PAGE); + + RNA_int_set(WM_keymap_add_item(keymap, "FONT_OT_change_spacing", LEFTARROWKEY, KM_PRESS, KM_ALT, 0)->ptr, "delta", -1); + RNA_int_set(WM_keymap_add_item(keymap, "FONT_OT_change_spacing", RIGHTARROWKEY, KM_PRESS, KM_ALT, 0)->ptr, "delta", 1); + RNA_int_set(WM_keymap_add_item(keymap, "FONT_OT_change_character", UPARROWKEY, KM_PRESS, KM_ALT, 0)->ptr, "delta", 1); + RNA_int_set(WM_keymap_add_item(keymap, "FONT_OT_change_character", DOWNARROWKEY, KM_PRESS, KM_ALT, 0)->ptr, "delta", -1); + + WM_keymap_add_item(keymap, "FONT_OT_text_copy", CKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "FONT_OT_text_cut", XKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "FONT_OT_text_paste", PKEY, KM_PRESS, KM_CTRL, 0); + + WM_keymap_add_item(keymap, "FONT_OT_line_break", RETKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "FONT_OT_text_insert", KM_TEXTINPUT, KM_ANY, KM_ANY, 0); // last! + + /* only set in editmode curve, by space_view3d listener */ + keymap= WM_keymap_listbase(wm, "Curve", 0, 0); + + WM_keymap_add_item(keymap, "OBJECT_OT_curve_add", AKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "CURVE_OT_vertex_add", ACTIONMOUSE, KM_PRESS, KM_CTRL, 0); + + WM_keymap_add_item(keymap, "CURVE_OT_select_all_toggle", AKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "CURVE_OT_select_row", RKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "CURVE_OT_select_more", PADPLUSKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "CURVE_OT_select_less", PADMINUS, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "CURVE_OT_select_linked", LKEY, KM_PRESS, 0, 0); + RNA_boolean_set(WM_keymap_add_item(keymap, "CURVE_OT_select_linked", LKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "deselect", 1); + + WM_keymap_add_item(keymap, "CURVE_OT_separate", PKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "CURVE_OT_extrude", EKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "CURVE_OT_duplicate", DKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "CURVE_OT_make_segment", FKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "CURVE_OT_cyclic_toggle", CKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "CURVE_OT_delete", XKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "CURVE_OT_delete", DELKEY, KM_PRESS, 0, 0); + + WM_keymap_add_item(keymap, "CURVE_OT_tilt_clear", TKEY, KM_PRESS, KM_ALT, 0); + RNA_enum_set(WM_keymap_add_item(keymap, "TFM_OT_transform", TKEY, KM_PRESS, 0, 0)->ptr, "mode", TFM_TILT); + RNA_enum_set(WM_keymap_add_item(keymap, "CURVE_OT_handle_type_set", HKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", 1); + RNA_enum_set(WM_keymap_add_item(keymap, "CURVE_OT_handle_type_set", HKEY, KM_PRESS, 0, 0)->ptr, "type", 3); + RNA_enum_set(WM_keymap_add_item(keymap, "CURVE_OT_handle_type_set", VKEY, KM_PRESS, 0, 0)->ptr, "type", 2); + + WM_keymap_add_item(keymap, "CURVE_OT_reveal", HKEY, KM_PRESS, KM_ALT, 0); + WM_keymap_add_item(keymap, "CURVE_OT_hide", HKEY, KM_PRESS, KM_ALT|KM_CTRL, 0); + RNA_enum_set(WM_keymap_add_item(keymap, "CURVE_OT_hide", HKEY, KM_PRESS, KM_ALT|KM_SHIFT, 0)->ptr, "unselected", 1); + + WM_keymap_add_item(keymap, "CURVE_OT_specials_menu", WKEY, KM_PRESS, 0, 0); +} + diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c new file mode 100644 index 00000000000..45b9a589bd5 --- /dev/null +++ b/source/blender/editors/curve/editcurve.c @@ -0,0 +1,5235 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <math.h> +#include <string.h> + +#ifndef WIN32 +#include <unistd.h> +#else +#include <io.h> +#endif +#include <stdlib.h> +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_dynstr.h" +#include "BLI_rand.h" + +#include "DNA_curve_types.h" +#include "DNA_key_types.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" +#include "DNA_userdef_types.h" + +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_fcurve.h" +#include "BKE_global.h" +#include "BKE_key.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_report.h" +#include "BKE_utildefines.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_anim_api.h" +#include "ED_curve.h" +#include "ED_keyframes_edit.h" +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_types.h" +#include "ED_util.h" +#include "ED_view3d.h" + +#include "UI_interface.h" + +#include "BIF_transform.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +/* still need to eradicate a few :( */ +#define callocstructN(x,y,name) (x*)MEM_callocN((y)* sizeof(x),name) + +/* for curve objects in editmode that can have hidden handles */ +#define BEZSELECTED_HIDDENHANDLES(bezt) ((G.f & G_HIDDENHANDLES) ? (bezt)->f2 & SELECT : BEZSELECTED(bezt)) + +float nurbcircle[8][2]= { + {0.0, -1.0}, {-1.0, -1.0}, {-1.0, 0.0}, {-1.0, 1.0}, + {0.0, 1.0}, { 1.0, 1.0}, { 1.0, 0.0}, { 1.0, -1.0} +}; + +ListBase *curve_get_editcurve(Object *ob) +{ + if(ob && ELEM(ob->type, OB_CURVE, OB_SURF)) { + Curve *cu= ob->data; + return cu->editnurb; + } + return NULL; +} + +/* this replaces the active flag used in uv/face mode */ +void set_actNurb(Object *obedit, Nurb *nu) +{ + Curve *cu= obedit->data; + + if(nu==NULL) + cu->actnu = -1; + else + cu->actnu = BLI_findindex(cu->editnurb, nu); +} + +Nurb *get_actNurb(Object *obedit) +{ + Curve *cu= obedit->data; + + return BLI_findlink(cu->editnurb, cu->actnu); +} + +/* ******************* SELECTION FUNCTIONS ********************* */ + +#define HIDDEN 1 +#define VISIBLE 0 + +#define FIRST 1 +#define LAST 0 + + +/* returns 1 in case (de)selection was successful */ +static short select_beztriple(BezTriple *bezt, short selstatus, short flag, short hidden) +{ + if(bezt) { + if((bezt->hide==0) || (hidden==1)) { + if(selstatus==1) { /* selects */ + bezt->f1 |= flag; + bezt->f2 |= flag; + bezt->f3 |= flag; + return 1; + } + else { /* deselects */ + bezt->f1 &= ~flag; + bezt->f2 &= ~flag; + bezt->f3 &= ~flag; + return 1; + } + } + } + + return 0; +} + +/* returns 1 in case (de)selection was successful */ +static short select_bpoint(BPoint *bp, short selstatus, short flag, short hidden) +{ + if(bp) { + if((bp->hide==0) || (hidden==1)) { + if(selstatus==1) { + bp->f1 |= flag; + return 1; + } + else { + bp->f1 &= ~flag; + return 1; + } + } + } + + return 0; +} + +static short swap_selection_beztriple(BezTriple *bezt) +{ + if(bezt->f2 & SELECT) + return select_beztriple(bezt, DESELECT, 1, VISIBLE); + else + return select_beztriple(bezt, SELECT, 1, VISIBLE); +} + +static short swap_selection_bpoint(BPoint *bp) +{ + if(bp->f1 & SELECT) + return select_bpoint(bp, DESELECT, 1, VISIBLE); + else + return select_bpoint(bp, SELECT, 1, VISIBLE); +} + +short isNurbsel(Nurb *nu) +{ + BezTriple *bezt; + BPoint *bp; + int a; + + if((nu->type & 7)==CU_BEZIER) { + bezt= nu->bezt; + a= nu->pntsu; + while(a--) { + if( (bezt->f1 & SELECT) || (bezt->f2 & SELECT) || (bezt->f3 & SELECT) ) return 1; + bezt++; + } + } + else { + bp= nu->bp; + a= nu->pntsu*nu->pntsv; + while(a--) { + if( (bp->f1 & SELECT) ) return 1; + bp++; + } + } + return 0; +} + +int isNurbsel_count(Nurb *nu) +{ + BezTriple *bezt; + BPoint *bp; + int a, sel=0; + + if((nu->type & 7)==CU_BEZIER) { + bezt= nu->bezt; + a= nu->pntsu; + while(a--) { + if (BEZSELECTED_HIDDENHANDLES(bezt)) sel++; + bezt++; + } + } + else { + bp= nu->bp; + a= nu->pntsu*nu->pntsv; + while(a--) { + if( (bp->f1 & SELECT) ) sel++; + bp++; + } + } + return sel; +} + +/* ******************* PRINTS ********************* */ + +void printknots(Object *obedit) +{ + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + int a, num; + + for(nu= editnurb->first; nu; nu= nu->next) { + if(isNurbsel(nu) && (nu->type & 7)==CU_NURBS) { + if(nu->knotsu) { + num= KNOTSU(nu); + for(a=0;a<num;a++) printf("knotu %d: %f\n", a, nu->knotsu[a]); + } + if(nu->knotsv) { + num= KNOTSV(nu); + for(a=0;a<num;a++) printf("knotv %d: %f\n", a, nu->knotsv[a]); + } + } + } +} + +/* ********************* LOAD and MAKE *************** */ + +/* load editNurb in object */ +void load_editNurb(Object *obedit) +{ + ListBase *editnurb= curve_get_editcurve(obedit); + + if(obedit==NULL) return; + + if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + Curve *cu= obedit->data; + Nurb *nu, *newnu; + KeyBlock *actkey; + int totvert= count_curveverts(editnurb); + + /* are there keys? */ + actkey = ob_get_keyblock(obedit); + if(actkey) { + /* active key: the vertices */ + + if(totvert) { + if(actkey->data) MEM_freeN(actkey->data); + + actkey->data= MEM_callocN(cu->key->elemsize*totvert, "actkey->data"); + actkey->totelem= totvert; + + curve_to_key(cu, actkey, editnurb); + } + } + + if(cu->key && actkey!=cu->key->refkey) { + ; + } + else { + freeNurblist(&(cu->nurb)); + + for(nu= editnurb->first; nu; nu= nu->next) { + newnu= duplicateNurb(nu); + BLI_addtail(&(cu->nurb), newnu); + + if((nu->type & 7)==CU_NURBS) { + clamp_nurb_order_u(nu); + } + } + } + } + + set_actNurb(obedit, NULL); +} + +/* make copy in cu->editnurb */ +void make_editNurb(Object *obedit) +{ + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu, *newnu; + KeyBlock *actkey; + + if(obedit==NULL) return; + + if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + Curve *cu= obedit->data; + + if(editnurb) + freeNurblist(editnurb); + else + editnurb= cu->editnurb= MEM_callocN(sizeof(ListBase), "editnurb"); + + nu= cu->nurb.first; + cu->lastselbp= NULL; /* for select row */ + + while(nu) { + newnu= duplicateNurb(nu); + test2DNurb(newnu); // after join, or any other creation of curve + BLI_addtail(editnurb, newnu); + nu= nu->next; + } + + actkey = ob_get_keyblock(obedit); + if(actkey) { + // XXX strcpy(G.editModeTitleExtra, "(Key) "); + key_to_curve(actkey, cu, editnurb); + } + } + + set_actNurb(obedit, NULL); +} + +void free_editNurb(Object *obedit) +{ + Curve *cu= obedit->data; + + if(cu->editnurb) { + freeNurblist(cu->editnurb); + MEM_freeN(cu->editnurb); + cu->editnurb= NULL; + } +} + +/******************** separate operator ***********************/ + +static int separate_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + Nurb *nu, *nu1; + Object *oldob, *newob; + Base *oldbase, *newbase; + Curve *oldcu, *newcu; + ListBase *oldedit, *newedit; + + oldbase= CTX_data_active_base(C); + oldob= oldbase->object; + oldcu= oldob->data; + oldedit= oldcu->editnurb; + + if(oldcu->key) { + BKE_report(op->reports, RPT_ERROR, "Can't separate a curve with vertex keys."); + return OPERATOR_CANCELLED; + } + + WM_cursor_wait(1); + + /* 1. duplicate the object and data */ + newbase= ED_object_add_duplicate(scene, oldbase, 0); /* 0 = fully linked */ + ED_base_object_select(newbase, BA_DESELECT); + newob= newbase->object; + + newcu= newob->data= copy_curve(oldcu); + newcu->editnurb= NULL; + oldcu->id.us--; /* because new curve is a copy: reduce user count */ + + /* 2. put new object in editmode and clear it */ + make_editNurb(newob); + newedit= newcu->editnurb; + freeNurblist(newedit); + + /* 3. move over parts from old object */ + for(nu= oldedit->first; nu; nu=nu1) { + nu1= nu->next; + + if(isNurbsel(nu)) { + BLI_remlink(oldedit, nu); + BLI_addtail(newedit, nu); + } + } + + /* 4. put old object out of editmode */ + load_editNurb(newob); + free_editNurb(newob); + + DAG_object_flush_update(scene, oldob, OB_RECALC_DATA); /* this is the original one */ + DAG_object_flush_update(scene, newob, OB_RECALC_DATA); /* this is the separated one */ + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, oldob); + + WM_cursor_wait(0); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_separate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Separate"; + ot->idname= "CURVE_OT_separate"; + + /* api callbacks */ + ot->exec= separate_exec; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ******************* FLAGS ********************* */ + +static short isNurbselUV(Nurb *nu, int *u, int *v, int flag) +{ + /* return u!=-1: 1 row in u-direction selected. U has value between 0-pntsv + * return v!=-1: 1 collumn in v-direction selected. V has value between 0-pntsu + */ + BPoint *bp; + int a, b, sel; + + *u= *v= -1; + + bp= nu->bp; + for(b=0; b<nu->pntsv; b++) { + sel= 0; + for(a=0; a<nu->pntsu; a++, bp++) { + if(bp->f1 & flag) sel++; + } + if(sel==nu->pntsu) { + if(*u== -1) *u= b; + else return 0; + } + else if(sel>1) return 0; /* because sel==1 is still ok */ + } + + for(a=0; a<nu->pntsu; a++) { + sel= 0; + bp= nu->bp+a; + for(b=0; b<nu->pntsv; b++, bp+=nu->pntsu) { + if(bp->f1 & flag) sel++; + } + if(sel==nu->pntsv) { + if(*v== -1) *v= a; + else return 0; + } + else if(sel>1) return 0; + } + + if(*u==-1 && *v>-1) return 1; + if(*v==-1 && *u>-1) return 1; + return 0; +} + +static void setflagsNurb(ListBase *editnurb, short flag) +{ + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + int a; + + for(nu= editnurb->first; nu; nu= nu->next) { + if( (nu->type & 7)==CU_BEZIER) { + a= nu->pntsu; + bezt= nu->bezt; + while(a--) { + bezt->f1= bezt->f2= bezt->f3= flag; + bezt++; + } + } + else { + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a--) { + bp->f1= flag; + bp++; + } + } + } +} + +static void rotateflagNurb(ListBase *editnurb, short flag, float *cent, float rotmat[][3]) +{ + /* all verts with (flag & 'flag') rotate */ + Nurb *nu; + BPoint *bp; + int a; + + for(nu= editnurb->first; nu; nu= nu->next) { + if((nu->type & 7)==CU_NURBS) { + bp= nu->bp; + a= nu->pntsu*nu->pntsv; + + while(a--) { + if(bp->f1 & flag) { + bp->vec[0]-=cent[0]; + bp->vec[1]-=cent[1]; + bp->vec[2]-=cent[2]; + Mat3MulVecfl(rotmat, bp->vec); + bp->vec[0]+=cent[0]; + bp->vec[1]+=cent[1]; + bp->vec[2]+=cent[2]; + } + bp++; + } + } + } +} + +static void translateflagNurb(ListBase *editnurb, short flag, float *vec) +{ + /* all verts with ('flag' & flag) translate */ + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + int a; + + for(nu= editnurb->first; nu; nu= nu->next) { + if( (nu->type & 7)==CU_BEZIER) { + a= nu->pntsu; + bezt= nu->bezt; + while(a--) { + if(bezt->f1 & flag) VecAddf(bezt->vec[0], bezt->vec[0], vec); + if(bezt->f2 & flag) VecAddf(bezt->vec[1], bezt->vec[1], vec); + if(bezt->f3 & flag) VecAddf(bezt->vec[2], bezt->vec[2], vec); + bezt++; + } + } + else { + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a--) { + if(bp->f1 & flag) VecAddf(bp->vec, bp->vec, vec); + bp++; + } + } + + test2DNurb(nu); + } +} + +static void weightflagNurb(ListBase *editnurb, short flag, float w, int mode) /* mode==0: replace, mode==1: multiply */ +{ + Nurb *nu; + BPoint *bp; + int a; + + for(nu= editnurb->first; nu; nu= nu->next) { + if((nu->type & 7)==CU_NURBS) { + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a--) { + if(bp->f1 & flag) { + if(mode==1) bp->vec[3]*= w; + else bp->vec[3]= w; + } + bp++; + } + } + } +} + +static int deleteflagNurb(bContext *C, wmOperator *op, int flag) +{ + Object *obedit= CTX_data_edit_object(C); + Curve *cu= obedit->data; + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu, *next; + BPoint *bp, *bpn, *newbp; + int a, b, newu, newv, sel; + + if(obedit && obedit->type==OB_SURF); + else return OPERATOR_CANCELLED; + + cu->lastselbp= NULL; + + nu= editnurb->first; + while(nu) { + next= nu->next; + + /* is entire nurb selected */ + bp= nu->bp; + a= nu->pntsu*nu->pntsv; + while(a) { + a--; + if(bp->f1 & flag); + else break; + bp++; + } + if(a==0) { + BLI_remlink(editnurb, nu); + freeNurb(nu); nu=NULL; + } + else { + /* is nurb in U direction selected */ + newv= nu->pntsv; + bp= nu->bp; + for(b=0; b<nu->pntsv; b++) { + sel= 0; + for(a=0; a<nu->pntsu; a++, bp++) { + if(bp->f1 & flag) sel++; + } + if(sel==nu->pntsu) { + newv--; + } + else if(sel>=1) { + /* don't delete */ + break; + } + } + if(newv!=nu->pntsv && b==nu->pntsv) { + /* delete */ + bp= nu->bp; + bpn = newbp = + (BPoint*) MEM_mallocN(newv * nu->pntsu * sizeof(BPoint), "deleteNurb"); + for(b=0; b<nu->pntsv; b++) { + if((bp->f1 & flag)==0) { + memcpy(bpn, bp, nu->pntsu*sizeof(BPoint)); + bpn+= nu->pntsu; + } + bp+= nu->pntsu; + } + nu->pntsv= newv; + MEM_freeN(nu->bp); + nu->bp= newbp; + clamp_nurb_order_v(nu); + + makeknots(nu, 2); + } + else { + /* is the nurb in V direction selected */ + newu= nu->pntsu; + for(a=0; a<nu->pntsu; a++) { + bp= nu->bp+a; + sel= 0; + for(b=0; b<nu->pntsv; b++, bp+=nu->pntsu) { + if(bp->f1 & flag) sel++; + } + if(sel==nu->pntsv) { + newu--; + } + else if(sel>=1) { + /* don't delete */ + break; + } + } + if(newu!=nu->pntsu && a==nu->pntsu) { + /* delete */ + bp= nu->bp; + bpn = newbp = + (BPoint*) MEM_mallocN(newu * nu->pntsv * sizeof(BPoint), "deleteNurb"); + for(b=0; b<nu->pntsv; b++) { + for(a=0; a<nu->pntsu; a++, bp++) { + if((bp->f1 & flag)==0) { + *bpn= *bp; + bpn++; + } + } + } + MEM_freeN(nu->bp); + nu->bp= newbp; + if(newu==1 && nu->pntsv>1) { /* make a U spline */ + nu->pntsu= nu->pntsv; + nu->pntsv= 1; + SWAP(short, nu->orderu, nu->orderv); + clamp_nurb_order_u(nu); + if(nu->knotsv) MEM_freeN(nu->knotsv); + nu->knotsv= NULL; + } + else { + nu->pntsu= newu; + clamp_nurb_order_u(nu); + } + makeknots(nu, 1); + } + } + } + nu= next; + } + + return OPERATOR_FINISHED; +} + +/* only for OB_SURF */ +static short extrudeflagNurb(ListBase *editnurb, int flag) +{ + Nurb *nu; + BPoint *bp, *bpn, *newbp; + int ok= 0, a, u, v, len; + + nu= editnurb->first; + while(nu) { + + if(nu->pntsv==1) { + bp= nu->bp; + a= nu->pntsu; + while(a) { + if(bp->f1 & flag); + else break; + bp++; + a--; + } + if(a==0) { + ok= 1; + newbp = + (BPoint*)MEM_mallocN(2 * nu->pntsu * sizeof(BPoint), "extrudeNurb1"); + memcpy(newbp, nu->bp, nu->pntsu*sizeof(BPoint) ); + bp= newbp+ nu->pntsu; + memcpy(bp, nu->bp, nu->pntsu*sizeof(BPoint) ); + MEM_freeN(nu->bp); + nu->bp= newbp; + a= nu->pntsu; + while(a--) { + select_bpoint(bp, SELECT, flag, HIDDEN); + select_bpoint(newbp, DESELECT, flag, HIDDEN); + bp++; + newbp++; + } + + nu->pntsv= 2; + nu->orderv= 2; + makeknots(nu, 2); + } + } + else { + /* which row or collumn is selected */ + + if( isNurbselUV(nu, &u, &v, flag) ) { + + /* deselect all */ + bp= nu->bp; + a= nu->pntsu*nu->pntsv; + while(a--) { + select_bpoint(bp, DESELECT, flag, HIDDEN); + bp++; + } + + if(u==0 || u== nu->pntsv-1) { /* row in u-direction selected */ + ok= 1; + newbp = + (BPoint*) MEM_mallocN(nu->pntsu*(nu->pntsv + 1) + * sizeof(BPoint), "extrudeNurb1"); + if(u==0) { + len= nu->pntsv*nu->pntsu; + memcpy(newbp+nu->pntsu, nu->bp, len*sizeof(BPoint) ); + memcpy(newbp, nu->bp, nu->pntsu*sizeof(BPoint) ); + bp= newbp; + } + else { + len= nu->pntsv*nu->pntsu; + memcpy(newbp, nu->bp, len*sizeof(BPoint) ); + memcpy(newbp+len, nu->bp+len-nu->pntsu, nu->pntsu*sizeof(BPoint) ); + bp= newbp+len; + } + + a= nu->pntsu; + while(a--) { + select_bpoint(bp, SELECT, flag, HIDDEN); + bp++; + } + + MEM_freeN(nu->bp); + nu->bp= newbp; + nu->pntsv++; + makeknots(nu, 2); + } + else if(v==0 || v== nu->pntsu-1) { /* collumn in v-direction selected */ + ok= 1; + bpn = newbp = + (BPoint*) MEM_mallocN((nu->pntsu + 1) * nu->pntsv * sizeof(BPoint), "extrudeNurb1"); + bp= nu->bp; + + for(a=0; a<nu->pntsv; a++) { + if(v==0) { + *bpn= *bp; + bpn->f1 |= flag; + bpn++; + } + memcpy(bpn, bp, nu->pntsu*sizeof(BPoint)); + bp+= nu->pntsu; + bpn+= nu->pntsu; + if(v== nu->pntsu-1) { + *bpn= *(bp-1); + bpn->f1 |= flag; + bpn++; + } + } + + MEM_freeN(nu->bp); + nu->bp= newbp; + nu->pntsu++; + makeknots(nu, 1); + } + } + } + nu= nu->next; + } + + return ok; +} + +static void adduplicateflagNurb(Object *obedit, short flag) +{ + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu, *newnu; + BezTriple *bezt, *bezt1; + BPoint *bp, *bp1; + int a, b, starta, enda, newu, newv; + char *usel; + + nu= editnurb->last; + while(nu) { + if( (nu->type & 7)==CU_BEZIER) { + bezt= nu->bezt; + for(a=0; a<nu->pntsu; a++) { + enda= -1; + starta= a; + while( (bezt->f1 & flag) || (bezt->f2 & flag) || (bezt->f3 & flag) ) { + select_beztriple(bezt, DESELECT, flag, HIDDEN); + enda=a; + if(a>=nu->pntsu-1) break; + a++; + bezt++; + } + if(enda>=starta) { + newnu = (Nurb*)MEM_mallocN(sizeof(Nurb), "adduplicateN"); + memcpy(newnu, nu, sizeof(Nurb)); + BLI_addtail(editnurb, newnu); + set_actNurb(obedit, newnu); + newnu->pntsu= enda-starta+1; + newnu->bezt= + (BezTriple*)MEM_mallocN((enda - starta + 1) * sizeof(BezTriple), "adduplicateN"); + memcpy(newnu->bezt, nu->bezt+starta, newnu->pntsu*sizeof(BezTriple)); + + b= newnu->pntsu; + bezt1= newnu->bezt; + while(b--) { + select_beztriple(bezt1, SELECT, flag, HIDDEN); + bezt1++; + } + + if(nu->flagu & CU_CYCLIC) { + if(starta!=0 || enda!=nu->pntsu-1) { + newnu->flagu &= ~CU_CYCLIC; + } + } + } + bezt++; + } + } + else if(nu->pntsv==1) { /* because UV Nurb has a different method for dupli */ + bp= nu->bp; + for(a=0; a<nu->pntsu; a++) { + enda= -1; + starta= a; + while(bp->f1 & flag) { + select_bpoint(bp, DESELECT, flag, HIDDEN); + enda= a; + if(a>=nu->pntsu-1) break; + a++; + bp++; + } + if(enda>=starta) { + newnu = (Nurb*)MEM_mallocN(sizeof(Nurb), "adduplicateN3"); + memcpy(newnu, nu, sizeof(Nurb)); + set_actNurb(obedit, newnu); + BLI_addtail(editnurb, newnu); + newnu->pntsu= enda-starta+1; + newnu->bp = (BPoint*)MEM_mallocN((enda-starta+1) * sizeof(BPoint), "adduplicateN4"); + memcpy(newnu->bp, nu->bp+starta, newnu->pntsu*sizeof(BPoint)); + + b= newnu->pntsu; + bp1= newnu->bp; + while(b--) { + select_bpoint(bp1, SELECT, flag, HIDDEN); + bp1++; + } + + if(nu->flagu & CU_CYCLIC) { + if(starta!=0 || enda!=nu->pntsu-1) { + newnu->flagu &= ~CU_CYCLIC; + } + } + + /* knots */ + newnu->knotsu= NULL; + makeknots(newnu, 1); + } + bp++; + } + } + else { + /* a rectangular area in nurb has to be selected */ + if(isNurbsel(nu)) { + usel= MEM_callocN(nu->pntsu, "adduplicateN4"); + bp= nu->bp; + for(a=0; a<nu->pntsv; a++) { + for(b=0; b<nu->pntsu; b++, bp++) { + if(bp->f1 & flag) usel[b]++; + } + } + newu= 0; + newv= 0; + for(a=0; a<nu->pntsu; a++) { + if(usel[a]) { + if(newv==0 || usel[a]==newv) { + newv= usel[a]; + newu++; + } + else { + newv= 0; + break; + } + } + } + if(newu==0 || newv==0) { + printf("Can't duplicate Nurb\n"); + } + else { + + if(newu==1) SWAP(short, newu, newv); + + newnu = (Nurb*)MEM_mallocN(sizeof(Nurb), "adduplicateN5"); + memcpy(newnu, nu, sizeof(Nurb)); + BLI_addtail(editnurb, newnu); + set_actNurb(obedit, newnu); + newnu->pntsu= newu; + newnu->pntsv= newv; + newnu->bp = + (BPoint*)MEM_mallocN(newu * newv * sizeof(BPoint), "adduplicateN6"); + clamp_nurb_order_u(newnu); + clamp_nurb_order_v(newnu); + + newnu->knotsu= newnu->knotsv= NULL; + + bp= newnu->bp; + bp1= nu->bp; + for(a=0; a<nu->pntsv; a++) { + for(b=0; b<nu->pntsu; b++, bp1++) { + if(bp1->f1 & flag) { + memcpy(bp, bp1, sizeof(BPoint)); + select_bpoint(bp1, DESELECT, flag, HIDDEN); + bp++; + } + } + } + if (check_valid_nurb_u(newnu)) { + if(nu->pntsu==newnu->pntsu && nu->knotsu) { + newnu->knotsu= MEM_dupallocN( nu->knotsu ); + } else { + makeknots(newnu, 1); + } + } + if (check_valid_nurb_v(newnu)) { + if(nu->pntsv==newnu->pntsv && nu->knotsv) { + newnu->knotsv= MEM_dupallocN( nu->knotsv ); + } else { + makeknots(newnu, 2); + } + } + } + MEM_freeN(usel); + } + } + + nu= nu->prev; + } + + /* actnu changed */ +} + +/**************** switch direction operator ***************/ + +static int switch_direction_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + + for(nu= editnurb->first; nu; nu= nu->next) + if(isNurbsel(nu)) + switchdirectionNurb(nu); + + DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_switch_direction(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Switch Direction"; + ot->idname= "CURVE_OT_switch_direction"; + + /* api callbacks */ + ot->exec= switch_direction_exec; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/****************** set weight operator *******************/ + +static int set_weight_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + float weight= RNA_float_get(op->ptr, "weight"); + int a; + + for(nu= editnurb->first; nu; nu= nu->next) { + if(nu->bezt) { + for(bezt=nu->bezt, a=0; a<nu->pntsu; a++, bezt++) { + if(bezt->f2 & SELECT) + bezt->weight= weight; + } + } + else if(nu->bp) { + for(bp=nu->bp, a=0; a<nu->pntsu*nu->pntsv; a++, bp++) { + if(bp->f1 & SELECT) + bp->weight= weight; + } + } + } + + DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_spline_weight_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Set Curve Weight"; + ot->idname= "CURVE_OT_spline_weight_set"; + + /* api callbacks */ + ot->exec= set_weight_exec; + ot->invoke= WM_operator_redo; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_float_percentage(ot->srna, "weight", 1.0f, 0.0f, 1.0f, "Weight", "", 0.0f, 1.0f); +} + +/******************* set radius operator ******************/ + +static int set_radius_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + float radius= RNA_float_get(op->ptr, "radius"); + int a; + + for(nu= editnurb->first; nu; nu= nu->next) { + if(nu->bezt) { + for(bezt=nu->bezt, a=0; a<nu->pntsu; a++, bezt++) { + if(bezt->f2 & SELECT) + bezt->radius= radius; + } + } + else if(nu->bp) { + for(bp=nu->bp, a=0; a<nu->pntsu*nu->pntsv; a++, bp++) { + if(bp->f1 & SELECT) + bp->radius= radius; + } + } + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_radius_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Set Curve Radius"; + ot->idname= "CURVE_OT_radius_set"; + + /* api callbacks */ + ot->exec= set_radius_exec; + ot->invoke= WM_operator_redo; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_float(ot->srna, "radius", 1.0f, 0.0f, FLT_MAX, "Radius", "", 0.0001f, 10.0f); +} + +/********************* smooth operator ********************/ + +static int smooth_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BezTriple *bezt, *beztOrig; + BPoint *bp, *bpOrig; + float val, newval, offset; + int a, i, change = 0; + + for(nu= editnurb->first; nu; nu= nu->next) { + if(nu->bezt) { + change = 0; + beztOrig = MEM_dupallocN( nu->bezt ); + for(bezt=nu->bezt+1, a=1; a<nu->pntsu-1; a++, bezt++) { + if(bezt->f2 & SELECT) { + for(i=0; i<3; i++) { + val = bezt->vec[1][i]; + newval = ((beztOrig+(a-1))->vec[1][i] * 0.5) + ((beztOrig+(a+1))->vec[1][i] * 0.5); + offset = (val*((1.0/6.0)*5)) + (newval*(1.0/6.0)) - val; + /* offset handles */ + bezt->vec[1][i] += offset; + bezt->vec[0][i] += offset; + bezt->vec[2][i] += offset; + } + change = 1; + } + } + MEM_freeN(beztOrig); + if (change) + calchandlesNurb(nu); + } else if (nu->bp) { + bpOrig = MEM_dupallocN( nu->bp ); + /* Same as above, keep these the same! */ + for(bp=nu->bp+1, a=1; a<nu->pntsu-1; a++, bp++) { + if(bp->f1 & SELECT) { + for(i=0; i<3; i++) { + val = bp->vec[i]; + newval = ((bpOrig+(a-1))->vec[i] * 0.5) + ((bpOrig+(a+1))->vec[i] * 0.5); + offset = (val*((1.0/6.0)*5)) + (newval*(1.0/6.0)) - val; + + bp->vec[i] += offset; + } + } + } + MEM_freeN(bpOrig); + } + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_smooth(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Smooth"; + ot->idname= "CURVE_OT_smooth"; + + /* api callbacks */ + ot->exec= smooth_exec; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/**************** smooth curve radius operator *************/ + +/* TODO, make smoothing distance based */ +static int smooth_radius_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + int a; + + /* use for smoothing */ + int last_sel; + int start_sel, end_sel; /* selection indicies, inclusive */ + float start_rad, end_rad, fac, range; + + for(nu= editnurb->first; nu; nu= nu->next) { + if(nu->bezt) { + + for (last_sel=0; last_sel < nu->pntsu; last_sel++) { + /* loop over selection segments of a curve, smooth each */ + + /* Start BezTriple code, this is duplicated below for points, make sure these functions stay in sync */ + start_sel = end_sel = -1; + for(bezt=nu->bezt+last_sel, a=last_sel; a<nu->pntsu; a++, bezt++) { + if(bezt->f2 & SELECT) { + start_sel = a; + break; + } + } + /* incase there are no other selected verts */ + end_sel = start_sel; + for(bezt=nu->bezt+(start_sel+1), a=start_sel+1; a<nu->pntsu; a++, bezt++) { + if((bezt->f2 & SELECT)==0) { + break; + } + end_sel = a; + } + + if (start_sel == -1) { + last_sel = nu->pntsu; /* next... */ + } else { + last_sel = end_sel; /* before we modify it */ + + /* now blend between start and end sel */ + start_rad = end_rad = -1.0; + + if (start_sel == end_sel) { + /* simple, only 1 point selected */ + if (start_sel>0) start_rad = (nu->bezt+start_sel-1)->radius; + if (end_sel!=-1 && end_sel < nu->pntsu) end_rad = (nu->bezt+start_sel+1)->radius; + + if (start_rad >= 0.0 && end_rad >= 0.0) (nu->bezt+start_sel)->radius = (start_rad + end_rad)/2; + else if (start_rad >= 0.0) (nu->bezt+start_sel)->radius = start_rad; + else if (end_rad >= 0.0) (nu->bezt+start_sel)->radius = end_rad; + } else { + /* if endpoints selected, then use them */ + if (start_sel==0) { + start_rad = (nu->bezt+start_sel)->radius; + start_sel++; /* we dont want to edit the selected endpoint */ + } else { + start_rad = (nu->bezt+start_sel-1)->radius; + } + if (end_sel==nu->pntsu-1) { + end_rad = (nu->bezt+end_sel)->radius; + end_sel--; /* we dont want to edit the selected endpoint */ + } else { + end_rad = (nu->bezt+end_sel+1)->radius; + } + + /* Now Blend between the points */ + range = (float)(end_sel - start_sel) + 2.0f; + for(bezt=nu->bezt+start_sel, a=start_sel; a<=end_sel; a++, bezt++) { + fac = (float)(1+a-start_sel) / range; + bezt->radius = start_rad*(1.0-fac) + end_rad*fac; + } + } + } + } + } else if (nu->bp) { + /* Same as above, keep these the same! */ + for (last_sel=0; last_sel < nu->pntsu; last_sel++) { + /* loop over selection segments of a curve, smooth each */ + + /* Start BezTriple code, this is duplicated below for points, make sure these functions stay in sync */ + start_sel = end_sel = -1; + for(bp=nu->bp+last_sel, a=last_sel; a<nu->pntsu; a++, bp++) { + if(bp->f1 & SELECT) { + start_sel = a; + break; + } + } + /* incase there are no other selected verts */ + end_sel = start_sel; + for(bp=nu->bp+(start_sel+1), a=start_sel+1; a<nu->pntsu; a++, bp++) { + if((bp->f1 & SELECT)==0) { + break; + } + end_sel = a; + } + + if (start_sel == -1) { + last_sel = nu->pntsu; /* next... */ + } else { + last_sel = end_sel; /* before we modify it */ + + /* now blend between start and end sel */ + start_rad = end_rad = -1.0; + + if (start_sel == end_sel) { + /* simple, only 1 point selected */ + if (start_sel>0) start_rad = (nu->bp+start_sel-1)->radius; + if (end_sel!=-1 && end_sel < nu->pntsu) end_rad = (nu->bp+start_sel+1)->radius; + + if (start_rad >= 0.0 && end_rad >= 0.0) (nu->bp+start_sel)->radius = (start_rad + end_rad)/2; + else if (start_rad >= 0.0) (nu->bp+start_sel)->radius = start_rad; + else if (end_rad >= 0.0) (nu->bp+start_sel)->radius = end_rad; + } else { + /* if endpoints selected, then use them */ + if (start_sel==0) { + start_rad = (nu->bp+start_sel)->radius; + start_sel++; /* we dont want to edit the selected endpoint */ + } else { + start_rad = (nu->bp+start_sel-1)->radius; + } + if (end_sel==nu->pntsu-1) { + end_rad = (nu->bp+end_sel)->radius; + end_sel--; /* we dont want to edit the selected endpoint */ + } else { + end_rad = (nu->bp+end_sel+1)->radius; + } + + /* Now Blend between the points */ + range = (float)(end_sel - start_sel) + 2.0f; + for(bp=nu->bp+start_sel, a=start_sel; a<=end_sel; a++, bp++) { + fac = (float)(1+a-start_sel) / range; + bp->radius = start_rad*(1.0-fac) + end_rad*fac; + } + } + } + } + } + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_smooth_radius(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Smooth Curve Radius"; + ot->idname= "CURVE_OT_smooth_radius"; + + /* api clastbacks */ + ot->exec= smooth_radius_exec; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/***************** selection utility *************************/ + +/* next == 1 -> select next */ +/* next == -1 -> select previous */ +/* cont == 1 -> select continuously */ +/* selstatus, inverts behaviour */ +static void select_adjacent_cp(ListBase *editnurb, short next, short cont, short selstatus) +{ + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + int a; + short lastsel= 0, sel=0; + + if(next==0) return; + + for(nu= editnurb->first; nu; nu= nu->next) { + lastsel=0; + if((nu->type & 7)==CU_BEZIER) { + a= nu->pntsu; + bezt= nu->bezt; + if(next < 0) bezt= (nu->bezt + (a-1)); + while(a--) { + if(a-abs(next) < 0) break; + sel= 0; + if((lastsel==0) && (bezt->hide==0) && ((bezt->f2 & SELECT) || (selstatus==0))) { + bezt+=next; + if(!(bezt->f2 & SELECT) || (selstatus==0)) { + sel= select_beztriple(bezt, selstatus, 1, VISIBLE); + if((sel==1) && (cont==0)) lastsel= 1; + } + } + else { + bezt+=next; + lastsel= 0; + } + /* move around in zigzag way so that we go through each */ + bezt-=(next-next/abs(next)); + } + } + else { + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + if(next < 0) bp= (nu->bp + (a-1)); + while(a--) { + if(a-abs(next) < 0) break; + sel=0; + if((lastsel==0) && (bp->hide==0) && ((bp->f1 & SELECT) || (selstatus==0))) { + bp+=next; + if(!(bp->f1 & SELECT) || (selstatus==0)) { + sel= select_bpoint(bp, selstatus, 1, VISIBLE); + if((sel==1) && (cont==0)) lastsel= 1; + } + } + else { + bp+=next; + lastsel= 0; + } + /* move around in zigzag way so that we go through each */ + bp-=(next-next/abs(next)); + } + } + } +} + +/**************** select start/end operators **************/ + +/* (de)selects first or last of visible part of each Nurb depending on selFirst */ +/* selFirst: defines the end of which to select */ +/* doswap: defines if selection state of each first/last control point is swapped */ +/* selstatus: selection status in case doswap is false */ +void selectend_nurb(Object *obedit, short selfirst, short doswap, short selstatus) +{ + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BPoint *bp; + BezTriple *bezt; + int a; + short sel; + + if(obedit==0) return; + + for(nu= editnurb->first; nu; nu= nu->next) { + sel= 0; + if((nu->type & 7)==CU_BEZIER) { + a= nu->pntsu; + + /* which point? */ + if(selfirst==0) { /* select last */ + bezt= (nu->bezt + (a-1)); + } + else { /* select first */ + bezt= nu->bezt; + } + + while(a--) { + if(doswap) sel= swap_selection_beztriple(bezt); + else sel= select_beztriple(bezt, selstatus, 1, VISIBLE); + + if(sel==1) break; + } + } + else { + a= nu->pntsu*nu->pntsv; + + /* which point? */ + if(selfirst==0) { /* select last */ + bp= (nu->bp + (a-1)); + } + else{ /* select first */ + bp= nu->bp; + } + + while(a--) { + if (bp->hide == 0) { + if(doswap) sel= swap_selection_bpoint(bp); + else sel= select_bpoint(bp, selstatus, 1, VISIBLE); + + if(sel==1) break; + } + } + } + } +} + +static int de_select_first_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + + selectend_nurb(obedit, FIRST, 1, DESELECT); + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_de_select_first(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select or Deselect First"; + ot->idname= "CURVE_OT_de_select_first"; + + /* api cfirstbacks */ + ot->exec= de_select_first_exec; + ot->poll= ED_operator_editcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +static int de_select_last_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + + selectend_nurb(obedit, LAST, 1, DESELECT); + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_de_select_last(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select or Deselect Last"; + ot->idname= "CURVE_OT_de_select_last"; + + /* api clastbacks */ + ot->exec= de_select_last_exec; + ot->poll= ED_operator_editcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/******************* de select all operator ***************/ + +static short nurb_has_selected_cps(ListBase *editnurb) +{ + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + int a; + + for(nu= editnurb->first; nu; nu= nu->next) { + if((nu->type & 7)==CU_BEZIER) { + a= nu->pntsu; + bezt= nu->bezt; + while(a--) { + if(bezt->hide==0) { + if((bezt->f1 & SELECT) + || (bezt->f2 & SELECT) + || (bezt->f3 & SELECT)) return 1; + } + bezt++; + } + } + else { + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a--) { + if((bp->hide==0) && (bp->f1 & SELECT)) return 1; + bp++; + } + } + } + + return 0; +} + +static int de_select_all_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + + if(nurb_has_selected_cps(editnurb)) { /* deselect all */ + selectend_nurb(obedit, FIRST, 0, DESELECT); /* set first control points as unselected */ + select_adjacent_cp(editnurb, 1, 1, DESELECT); /* cascade selection */ + } + else { /* select all */ + selectend_nurb(obedit, FIRST, 0, SELECT); /* set first control points as selected */ + select_adjacent_cp(editnurb, 1, 1, SELECT); /* cascade selection */ + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_select_all_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select or Deselect All"; + ot->idname= "CURVE_OT_select_all_toggle"; + + /* api callbacks */ + ot->exec= de_select_all_exec; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/********************** hide operator *********************/ + +static int hide_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BPoint *bp; + BezTriple *bezt; + int a, sel, invert= RNA_boolean_get(op->ptr, "unselected"); + + for(nu= editnurb->first; nu; nu= nu->next) { + if((nu->type & 7)==CU_BEZIER) { + bezt= nu->bezt; + a= nu->pntsu; + sel= 0; + while(a--) { + if(invert == 0 && BEZSELECTED_HIDDENHANDLES(bezt)) { + select_beztriple(bezt, DESELECT, 1, HIDDEN); + bezt->hide= 1; + } + else if(invert && !BEZSELECTED_HIDDENHANDLES(bezt)) { + select_beztriple(bezt, DESELECT, 1, HIDDEN); + bezt->hide= 1; + } + if(bezt->hide) sel++; + bezt++; + } + if(sel==nu->pntsu) nu->hide= 1; + } + else { + bp= nu->bp; + a= nu->pntsu*nu->pntsv; + sel= 0; + while(a--) { + if(invert==0 && (bp->f1 & SELECT)) { + select_bpoint(bp, DESELECT, 1, HIDDEN); + bp->hide= 1; + } + else if(invert && (bp->f1 & SELECT)==0) { + select_bpoint(bp, DESELECT, 1, HIDDEN); + bp->hide= 1; + } + if(bp->hide) sel++; + bp++; + } + if(sel==nu->pntsu*nu->pntsv) nu->hide= 1; + } + } + + DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_hide(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Hide Selected"; + ot->idname= "CURVE_OT_hide"; + + /* api callbacks */ + ot->exec= hide_exec; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* props */ + RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected."); +} + +/********************** reveal operator *********************/ + +static int reveal_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BPoint *bp; + BezTriple *bezt; + int a; + + for(nu= editnurb->first; nu; nu= nu->next) { + nu->hide= 0; + if((nu->type & 7)==CU_BEZIER) { + bezt= nu->bezt; + a= nu->pntsu; + while(a--) { + if(bezt->hide) { + select_beztriple(bezt, SELECT, 1, HIDDEN); + bezt->hide= 0; + } + bezt++; + } + } + else { + bp= nu->bp; + a= nu->pntsu*nu->pntsv; + while(a--) { + if(bp->hide) { + select_bpoint(bp, SELECT, 1, HIDDEN); + bp->hide= 0; + } + bp++; + } + } + } + + DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_reveal(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Reveal Hidden"; + ot->idname= "CURVE_OT_reveal"; + + /* api callbacks */ + ot->exec= reveal_exec; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/********************** select invert operator *********************/ + +static int select_invert_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BPoint *bp; + BezTriple *bezt; + int a; + + for(nu= editnurb->first; nu; nu= nu->next) { + if((nu->type & 7)==CU_BEZIER) { + bezt= nu->bezt; + a= nu->pntsu; + while(a--) { + if(bezt->hide==0) { + bezt->f2 ^= SELECT; /* always do the center point */ + if ((G.f & G_HIDDENHANDLES)==0) { + bezt->f1 ^= SELECT; + bezt->f3 ^= SELECT; + } + } + bezt++; + } + } + else { + bp= nu->bp; + a= nu->pntsu*nu->pntsv; + while(a--) { + swap_selection_bpoint(bp); + bp++; + } + } + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_select_invert(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select Invert"; + ot->idname= "CURVE_OT_select_invert"; + + /* api callbacks */ + ot->exec= select_invert_exec; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/********************** subdivide operator *********************/ + +/** Divide the line segments associated with the currently selected + * curve nodes (Bezier or NURB). If there are no valid segment + * selections within the current selection, nothing happens. + * + * @deffunc subdividenurb subdivideNurb(void) + * @return Nothing + * @param None +*/ + +static int subdivide_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BezTriple *prevbezt, *bezt, *beztnew, *beztn; + BPoint *bp, *prevbp, *bpnew, *bpn; + float vec[15]; + int a, b, sel, amount, *usel, *vsel; + + // printf("*** subdivideNurb: entering subdivide\n"); + + for(nu= editnurb->first; nu; nu= nu->next) { + amount= 0; + if((nu->type & 7)==CU_BEZIER) { + /* + Insert a point into a 2D Bezier curve. + Endpoints are preserved. Otherwise, all selected and inserted points are + newly created. Old points are discarded. + */ + /* count */ + if(nu->flagu & CU_CYCLIC) { + a= nu->pntsu; + bezt= nu->bezt; + prevbezt= bezt+(a-1); + } + else { + a= nu->pntsu-1; + prevbezt= nu->bezt; + bezt= prevbezt+1; + } + while(a--) { + if( BEZSELECTED_HIDDENHANDLES(prevbezt) && BEZSELECTED_HIDDENHANDLES(bezt) ) amount++; + prevbezt= bezt; + bezt++; + } + + if(amount) { + /* insert */ + beztnew = + (BezTriple*)MEM_mallocN((amount + nu->pntsu) * sizeof(BezTriple), "subdivNurb"); + beztn= beztnew; + if(nu->flagu & CU_CYCLIC) { + a= nu->pntsu; + bezt= nu->bezt; + prevbezt= bezt+(a-1); + } + else { + a= nu->pntsu-1; + prevbezt= nu->bezt; + bezt= prevbezt+1; + } + while(a--) { + memcpy(beztn, prevbezt, sizeof(BezTriple)); + beztn++; + + if( BEZSELECTED_HIDDENHANDLES(prevbezt) && BEZSELECTED_HIDDENHANDLES(bezt) ) { + memcpy(beztn, bezt, sizeof(BezTriple)); + + /* midpoint subdividing */ + VecMidf(vec, prevbezt->vec[1], prevbezt->vec[2]); + VecMidf(vec+3, prevbezt->vec[2], bezt->vec[0]); + VecMidf(vec+6, bezt->vec[0], bezt->vec[1]); + + VecMidf(vec+9, vec, vec+3); + VecMidf(vec+12, vec+3, vec+6); + + /* change handle of prev beztn */ + VECCOPY((beztn-1)->vec[2], vec); + /* new point */ + VECCOPY(beztn->vec[0], vec+9); + VecMidf(beztn->vec[1], vec+9, vec+12); + VECCOPY(beztn->vec[2], vec+12); + /* handle of next bezt */ + if(a==0 && (nu->flagu & CU_CYCLIC)) {VECCOPY(beztnew->vec[0], vec+6);} + else {VECCOPY(bezt->vec[0], vec+6);} + + beztn->radius = (prevbezt->radius + bezt->radius)/2.0f; + beztn->weight = (prevbezt->weight + bezt->weight)/2.0f; + + beztn++; + } + + prevbezt= bezt; + bezt++; + } + /* last point */ + if((nu->flagu & CU_CYCLIC)==0) memcpy(beztn, prevbezt, sizeof(BezTriple)); + + MEM_freeN(nu->bezt); + nu->bezt= beztnew; + nu->pntsu+= amount; + + calchandlesNurb(nu); + } + } /* End of 'if((nu->type & 7)==CU_BEZIER)' */ + else if (nu->pntsv==1) { + /* + All flat lines (ie. co-planar), except flat Nurbs. Flat NURB curves + are handled together with the regular NURB plane division, as it + should be. I split it off just now, let's see if it is + stable... nzc 30-5-'00 + */ + /* count */ + if(nu->flagu & CU_CYCLIC) { + a= nu->pntsu; + bp= nu->bp; + prevbp= bp+(a-1); + } + else { + a= nu->pntsu-1; + prevbp= nu->bp; + bp= prevbp+1; + } + while(a--) { + if( (bp->f1 & SELECT) && (prevbp->f1 & SELECT) ) amount++; + prevbp= bp; + bp++; + } + + if(amount) { + /* insert */ + bpnew = + (BPoint*)MEM_mallocN((amount + nu->pntsu) * sizeof(BPoint), "subdivNurb2"); + bpn= bpnew; + + if(nu->flagu & CU_CYCLIC) { + a= nu->pntsu; + bp= nu->bp; + prevbp= bp+(a-1); + } + else { + a= nu->pntsu-1; + prevbp= nu->bp; + bp= prevbp+1; + } + while(a--) { + memcpy(bpn, prevbp, sizeof(BPoint)); + bpn++; + + if( (bp->f1 & SELECT) && (prevbp->f1 & SELECT) ) { + // printf("*** subdivideNurb: insert 'linear' point\n"); + memcpy(bpn, bp, sizeof(BPoint)); + bpn->vec[0]= (prevbp->vec[0]+bp->vec[0])/2.0; + bpn->vec[1]= (prevbp->vec[1]+bp->vec[1])/2.0; + bpn->vec[2]= (prevbp->vec[2]+bp->vec[2])/2.0; + bpn->vec[3]= (prevbp->vec[3]+bp->vec[3])/2.0; + bpn++; + + } + prevbp= bp; + bp++; + } + if((nu->flagu & CU_CYCLIC)==0) memcpy(bpn, prevbp, sizeof(BPoint)); /* last point */ + + MEM_freeN(nu->bp); + nu->bp= bpnew; + nu->pntsu+= amount; + + if(nu->type & CU_NURBS) { + makeknots(nu, 1); + } + } + } /* End of 'else if(nu->pntsv==1)' */ + else if((nu->type & 7)==CU_NURBS) { + /* This is a very strange test ... */ + /** + Subdivide NURB surfaces - nzc 30-5-'00 - + + Subdivision of a NURB curve can be effected by adding a + control point (insertion of a knot), or by raising the + degree of the functions used to build the NURB. The + expression + + degree = #knots - #controlpoints + 1 (J Walter piece) + degree = #knots - #controlpoints (Blender + implementation) + ( this is confusing.... what is true? Another concern + is that the JW piece allows the curve to become + explicitly 1st order derivative discontinuous, while + this is not what we want here... ) + + is an invariant for a single NURB curve. Raising the degree + of the NURB is done elsewhere; the degree is assumed + constant during this opration. Degree is a property shared + by all controlpoints in a curve (even though it is stored + per control point - this can be misleading). + Adding a knot is done by searching for the place in the + knot vector where a certain knot value must be inserted, or + by picking an appropriate knot value between two existing + ones. The number of controlpoints that is influenced by the + insertion depends on the order of the curve. A certain + minimum number of knots is needed to form high-order + curves, as can be seen from the equation above. In Blender, + currently NURBs may be up to 6th order, so we modify at + most 6 points. One point is added. For an n-degree curve, + n points are discarded, and n+1 points inserted + (so effectively, n points are modified). (that holds for + the JW piece, but it seems not for our NURBs) + In practice, the knot spacing is copied, but the tail + (the points following the insertion point) need to be + offset to keep the knot series ascending. The knot series + is always a series of monotonically ascending integers in + Blender. When not enough control points are available to + fit the order, duplicates of the endpoints are added as + needed. + */ + /* selection-arrays */ + usel= MEM_callocN(sizeof(int)*nu->pntsu, "subivideNurb3"); + vsel= MEM_callocN(sizeof(int)*nu->pntsv, "subivideNurb3"); + sel= 0; + + /* Count the number of selected points. */ + bp= nu->bp; + for(a=0; a<nu->pntsv; a++) { + for(b=0; b<nu->pntsu; b++) { + if(bp->f1 & SELECT) { + usel[b]++; + vsel[a]++; + sel++; + } + bp++; + } + } + if( sel == (nu->pntsu*nu->pntsv) ) { /* subdivide entire nurb */ + /* Global subdivision is a special case of partial + subdivision. Strange it is considered separately... */ + bpn=bpnew= MEM_mallocN( (2*nu->pntsu-1)*(2*nu->pntsv-1)*sizeof(BPoint), "subdivideNurb4"); + bp= nu->bp; + /* first subdivide rows */ + for(a=0; a<nu->pntsv; a++) { + for(b=0; b<nu->pntsu; b++) { + *bpn= *bp; + bpn++; + bp++; + if(b<nu->pntsu-1) { + *bpn= *bp; + prevbp= bp-1; + bpn->vec[0]= (prevbp->vec[0]+bp->vec[0])/2.0; + bpn->vec[1]= (prevbp->vec[1]+bp->vec[1])/2.0; + bpn->vec[2]= (prevbp->vec[2]+bp->vec[2])/2.0; + bpn->vec[3]= (prevbp->vec[3]+bp->vec[3])/2.0; + bpn++; + } + } + bpn+= (2*nu->pntsu-1); + } + /* now insert new */ + bpn= bpnew+(2*nu->pntsu-1); + bp= bpnew+(4*nu->pntsu-2); + prevbp= bpnew; + for(a=1; a<nu->pntsv; a++) { + + for(b=0; b<2*nu->pntsu-1; b++) { + *bpn= *bp; + bpn->vec[0]= (prevbp->vec[0]+bp->vec[0])/2.0; + bpn->vec[1]= (prevbp->vec[1]+bp->vec[1])/2.0; + bpn->vec[2]= (prevbp->vec[2]+bp->vec[2])/2.0; + bpn->vec[3]= (prevbp->vec[3]+bp->vec[3])/2.0; + bpn++; + bp++; + prevbp++; + } + bp+= (2*nu->pntsu-1); + bpn+= (2*nu->pntsu-1); + prevbp+= (2*nu->pntsu-1); + } + MEM_freeN(nu->bp); + nu->bp= bpnew; + nu->pntsu= 2*nu->pntsu-1; + nu->pntsv= 2*nu->pntsv-1; + makeknots(nu, 1); + makeknots(nu, 2); + } /* End of 'if(sel== nu->pntsu*nu->pntsv)' (subdivide entire NURB) */ + else { + /* subdivide in v direction? */ + sel= 0; + for(a=0; a<nu->pntsv-1; a++) { + if(vsel[a]==nu->pntsu && vsel[a+1]==nu->pntsu) sel++; + } + + if(sel) { /* V ! */ + bpn=bpnew= MEM_mallocN( (sel+nu->pntsv)*nu->pntsu*sizeof(BPoint), "subdivideNurb4"); + bp= nu->bp; + for(a=0; a<nu->pntsv; a++) { + for(b=0; b<nu->pntsu; b++) { + *bpn= *bp; + bpn++; + bp++; + } + if( (a<nu->pntsv-1) && vsel[a]==nu->pntsu && vsel[a+1]==nu->pntsu ) { + prevbp= bp- nu->pntsu; + for(b=0; b<nu->pntsu; b++) { + /* + This simple bisection must be replaces by a + subtle resampling of a number of points. Our + task is made slightly easier because each + point in our curve is a separate data + node. (is it?) + */ + *bpn= *prevbp; + bpn->vec[0]= (prevbp->vec[0]+bp->vec[0])/2.0; + bpn->vec[1]= (prevbp->vec[1]+bp->vec[1])/2.0; + bpn->vec[2]= (prevbp->vec[2]+bp->vec[2])/2.0; + bpn->vec[3]= (prevbp->vec[3]+bp->vec[3])/2.0; + bpn++; + prevbp++; + bp++; + } + bp-= nu->pntsu; + } + } + MEM_freeN(nu->bp); + nu->bp= bpnew; + nu->pntsv+= sel; + makeknots(nu, 2); + } + else { + /* or in u direction? */ + sel= 0; + for(a=0; a<nu->pntsu-1; a++) { + if(usel[a]==nu->pntsv && usel[a+1]==nu->pntsv) sel++; + } + + if(sel) { /* U ! */ + /* Inserting U points is sort of 'default' Flat curves only get */ + /* U points inserted in them. */ + bpn=bpnew= MEM_mallocN( (sel+nu->pntsu)*nu->pntsv*sizeof(BPoint), "subdivideNurb4"); + bp= nu->bp; + for(a=0; a<nu->pntsv; a++) { + for(b=0; b<nu->pntsu; b++) { + *bpn= *bp; + bpn++; + bp++; + if( (b<nu->pntsu-1) && usel[b]==nu->pntsv && usel[b+1]==nu->pntsv ) { + /* + One thing that bugs me here is that the + orders of things are not the same as in + the JW piece. Also, this implies that we + handle at most 3rd order curves? I miss + some symmetry here... + */ + prevbp= bp- 1; + *bpn= *prevbp; + bpn->vec[0]= (prevbp->vec[0]+bp->vec[0])/2.0; + bpn->vec[1]= (prevbp->vec[1]+bp->vec[1])/2.0; + bpn->vec[2]= (prevbp->vec[2]+bp->vec[2])/2.0; + bpn->vec[3]= (prevbp->vec[3]+bp->vec[3])/2.0; + bpn++; + } + } + } + MEM_freeN(nu->bp); + nu->bp= bpnew; + nu->pntsu+= sel; + makeknots(nu, 1); /* shift knots + forward */ + } + } + } + MEM_freeN(usel); + MEM_freeN(vsel); + + } /* End of 'if((nu->type & 7)==CU_NURBS)' */ + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_subdivide(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Subdivide"; + ot->idname= "CURVE_OT_subdivide"; + + /* api callbacks */ + ot->exec= subdivide_exec; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/******************** find nearest ************************/ + +static void findnearestNurbvert__doClosest(void *userData, Nurb *nu, BPoint *bp, BezTriple *bezt, int beztindex, int x, int y) +{ + struct { BPoint *bp; BezTriple *bezt; Nurb *nurb; int dist, hpoint, select, mval[2]; } *data = userData; + + short flag; + short temp; + + if (bp) { + flag = bp->f1; + } else { + if (beztindex==0) { + flag = bezt->f1; + } else if (beztindex==1) { + flag = bezt->f2; + } else { + flag = bezt->f3; + } + } + + temp = abs(data->mval[0]-x) + abs(data->mval[1]-y); + if ((flag&1)==data->select) temp += 5; + if (bezt && beztindex==1) temp += 3; /* middle points get a small disadvantage */ + + if (temp<data->dist) { + data->dist = temp; + + data->bp = bp; + data->bezt = bezt; + data->nurb = nu; + data->hpoint = bezt?beztindex:0; + } +} + +static short findnearestNurbvert(ViewContext *vc, short sel, int mval[2], Nurb **nurb, BezTriple **bezt, BPoint **bp) +{ + /* sel==1: selected gets a disadvantage */ + /* in nurb and bezt or bp the nearest is written */ + /* return 0 1 2: handlepunt */ + struct { BPoint *bp; BezTriple *bezt; Nurb *nurb; int dist, hpoint, select, mval[2]; } data = {0}; + + data.dist = 100; + data.hpoint = 0; + data.select = sel; + data.mval[0] = mval[0]; + data.mval[1] = mval[1]; + + nurbs_foreachScreenVert(vc, findnearestNurbvert__doClosest, &data); + + *nurb = data.nurb; + *bezt = data.bezt; + *bp = data.bp; + + return data.hpoint; +} + +static void findselectedNurbvert(ListBase *editnurb, Nurb **nu, BezTriple **bezt, BPoint **bp) +{ + /* in nu and (bezt or bp) selected are written if there's 1 sel. */ + /* if more points selected in 1 spline: return only nu, bezt and bp are 0 */ + Nurb *nu1; + BezTriple *bezt1; + BPoint *bp1; + int a; + + *nu= 0; + *bezt= 0; + *bp= 0; + for(nu1= editnurb->first; nu1; nu1= nu1->next) { + if((nu1->type & 7)==CU_BEZIER) { + bezt1= nu1->bezt; + a= nu1->pntsu; + while(a--) { + if( (bezt1->f1 & SELECT) || (bezt1->f2 & SELECT) || (bezt1->f3 & SELECT) ) { + if(*nu!=0 && *nu!= nu1) { + *nu= 0; + *bp= 0; + *bezt= 0; + return; + } + else if(*bezt || *bp) { + *bp= 0; + *bezt= 0; + } + else { + *bezt= bezt1; + *nu= nu1; + } + } + bezt1++; + } + } + else { + bp1= nu1->bp; + a= nu1->pntsu*nu1->pntsv; + while(a--) { + if( bp1->f1 & 1 ) { + if(*nu!=0 && *nu!= nu1) { + *bp= 0; + *bezt= 0; + *nu= 0; + return; + } + else if(*bezt || *bp) { + *bp= 0; + *bezt= 0; + } + else { + *bp= bp1; + *nu= nu1; + } + } + bp1++; + } + } + } +} + +/***************** set spline type operator *******************/ + +static int convertspline(short type, Nurb *nu) +{ + BezTriple *bezt; + BPoint *bp; + int a, c, nr; + + if((nu->type & 7)==CU_POLY) { + if(type==CU_BEZIER) { /* to Bezier with vecthandles */ + nr= nu->pntsu; + bezt = + (BezTriple*)MEM_callocN(nr * sizeof(BezTriple), "setsplinetype2"); + nu->bezt= bezt; + a= nr; + bp= nu->bp; + while(a--) { + VECCOPY(bezt->vec[1], bp->vec); + bezt->f1=bezt->f2=bezt->f3= bp->f1; + bezt->h1= bezt->h2= HD_VECT; + bezt->weight= bp->weight; + bezt->radius= bp->radius; + bp++; + bezt++; + } + MEM_freeN(nu->bp); + nu->bp= 0; + nu->pntsu= nr; + nu->type &= ~7; + nu->type |= CU_BEZIER; + calchandlesNurb(nu); + } + else if(type==CU_NURBS) { + nu->type &= ~7; + nu->type |= CU_NURBS; + nu->orderu= 4; + nu->flagu &= CU_CYCLIC; /* disable all flags except for cyclic */ + nu->flagu += 4; + makeknots(nu, 1); + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a--) { + bp->vec[3]= 1.0; + bp++; + } + } + } + else if((nu->type & 7)==CU_BEZIER) { /* Bezier */ + if(type==0 || type==4) { /* to Poly or Nurb */ + nr= 3*nu->pntsu; + nu->bp = MEM_callocN(nr * sizeof(BPoint), "setsplinetype"); + a= nu->pntsu; + bezt= nu->bezt; + bp= nu->bp; + while(a--) { + if(type==0 && bezt->h1==HD_VECT && bezt->h2==HD_VECT) { + /* vector handle becomes 1 poly vertice */ + VECCOPY(bp->vec, bezt->vec[1]); + bp->vec[3]= 1.0; + bp->f1= bezt->f2; + nr-= 2; + bp->radius= bezt->radius; + bp->weight= bezt->weight; + bp++; + } + else { + for(c=0;c<3;c++) { + VECCOPY(bp->vec, bezt->vec[c]); + bp->vec[3]= 1.0; + if(c==0) bp->f1= bezt->f1; + else if(c==1) bp->f1= bezt->f2; + else bp->f1= bezt->f3; + bp->radius= bezt->radius; + bp->weight= bezt->weight; + bp++; + } + } + bezt++; + } + MEM_freeN(nu->bezt); + nu->bezt= 0; + nu->pntsu= nr; + nu->pntsv= 1; + nu->orderu= 4; + nu->orderv= 1; + nu->type &= ~7; + nu->type+= type; + if(nu->flagu & CU_CYCLIC) c= nu->orderu-1; + else c= 0; + if(type== 4) { + nu->flagu &= CU_CYCLIC; /* disable all flags except for cyclic */ + nu->flagu += 4; + makeknots(nu, 1); + } + } + } + else if((nu->type & 7)==CU_NURBS) { + if(type==0) { /* to Poly */ + nu->type &= ~7; + if(nu->knotsu) MEM_freeN(nu->knotsu); /* python created nurbs have a knotsu of zero */ + nu->knotsu= NULL; + if(nu->knotsv) MEM_freeN(nu->knotsv); + nu->knotsv= NULL; + } + else if(type==CU_BEZIER) { /* to Bezier */ + nr= nu->pntsu/3; + + if(nr<2) + return 1; /* conversion impossible */ + else { + bezt = MEM_callocN(nr * sizeof(BezTriple), "setsplinetype2"); + nu->bezt= bezt; + a= nr; + bp= nu->bp; + while(a--) { + VECCOPY(bezt->vec[0], bp->vec); + bezt->f1= bp->f1; + bp++; + VECCOPY(bezt->vec[1], bp->vec); + bezt->f2= bp->f1; + bp++; + VECCOPY(bezt->vec[2], bp->vec); + bezt->f3= bp->f1; + bezt->radius= bp->radius; + bezt->weight= bp->weight; + bp++; + bezt++; + } + MEM_freeN(nu->bp); + nu->bp= 0; + MEM_freeN(nu->knotsu); + nu->knotsu= NULL; + nu->pntsu= nr; + nu->type &= ~7; + nu->type |= CU_BEZIER; + } + } + } + + return 0; +} + +static int set_spline_type_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + int changed=0, type= RNA_enum_get(op->ptr, "type"); + + if(type==CU_CARDINAL || type==CU_BSPLINE) { + BKE_report(op->reports, RPT_ERROR, "Not implemented yet"); + return OPERATOR_CANCELLED; + } + + for(nu= editnurb->first; nu; nu= nu->next) { + if(isNurbsel(nu)) { + if(convertspline(type, nu)) + BKE_report(op->reports, RPT_ERROR, "No conversion possible"); + else + changed= 1; + } + } + + return (changed)? OPERATOR_FINISHED: OPERATOR_CANCELLED; +} + +void CURVE_OT_spline_type_set(wmOperatorType *ot) +{ + static EnumPropertyItem type_items[]= { + {CU_POLY, "POLY", "Poly", ""}, + {CU_BEZIER, "BEZIER", "Bezier", ""}, + {CU_CARDINAL, "CARDINAL", "Cardinal", ""}, + {CU_BSPLINE, "B_SPLINE", "B-Spline", ""}, + {CU_NURBS, "NURBS", "NURBS", ""}, + {0, NULL, NULL, NULL}}; + + /* identifiers */ + ot->name= "Set Spline Type"; + ot->idname= "CURVE_OT_spline_type_set"; + + /* api callbacks */ + ot->exec= set_spline_type_exec; + ot->poll= ED_operator_editcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, "type", type_items, CU_POLY, "Type", "Spline type"); +} + +/***************** set handle type operator *******************/ + +static int set_handle_type_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + + sethandlesNurb(editnurb, RNA_enum_get(op->ptr, "type")); + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_handle_type_set(wmOperatorType *ot) +{ + static EnumPropertyItem type_items[]= { + {1, "AUTOMATIC", "Automatic", ""}, + {2, "VECTOR", "Vector", ""}, + {3, "TOGGLE_FREE_ALIGN", "Toggle Free/Align", ""}, + {5, "ALIGN", "Align", ""}, + {6, "FREE_ALIGN", "Free Align", ""}, + {0, NULL, NULL, NULL}}; + + /* identifiers */ + ot->name= "Set Handle Type"; + ot->idname= "CURVE_OT_handle_type_set"; + + /* api callbacks */ + ot->exec= set_handle_type_exec; + ot->poll= ED_operator_editcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, "type", type_items, CU_POLY, "Type", "Spline type"); +} + +/***************** make segment operator **********************/ + +/* ******************** SKINNING LOFTING!!! ******************** */ + +static void switchdirection_knots(float *base, int tot) +{ + float *fp1, *fp2, *tempf; + int a; + + if(base==NULL || tot==0) return; + + /* reverse knots */ + a= tot; + fp1= base; + fp2= fp1+(a-1); + a/= 2; + while(fp1!=fp2 && a>0) { + SWAP(float, *fp1, *fp2); + a--; + fp1++; + fp2--; + } + /* and make in increasing order again */ + a= tot; + fp1= base; + fp2=tempf= MEM_mallocN(sizeof(float)*a, "switchdirect"); + while(a--) { + fp2[0]= fabs(fp1[1]-fp1[0]); + fp1++; + fp2++; + } + + a= tot-1; + fp1= base; + fp2= tempf; + fp1[0]= 0.0; + fp1++; + while(a--) { + fp1[0]= fp1[-1]+fp2[0]; + fp1++; + fp2++; + } + MEM_freeN(tempf); +} + +static void rotate_direction_nurb(Nurb *nu) +{ + BPoint *bp1, *bp2, *temp; + int u, v; + + SWAP(short, nu->pntsu, nu->pntsv); + SWAP(short, nu->orderu, nu->orderv); + SWAP(short, nu->resolu, nu->resolv); + SWAP(short, nu->flagu, nu->flagv); + + SWAP(float *, nu->knotsu, nu->knotsv); + switchdirection_knots(nu->knotsv, KNOTSV(nu) ); + + temp= MEM_dupallocN(nu->bp); + bp1= nu->bp; + for(v=0; v<nu->pntsv; v++) { + for(u=0; u<nu->pntsu; u++, bp1++) { + bp2= temp + (nu->pntsu-u-1)*(nu->pntsv) + v; + *bp1= *bp2; + } + } + + MEM_freeN(temp); +} + +static int is_u_selected(Nurb *nu, int u) +{ + BPoint *bp; + int v; + + /* what about resolu == 2? */ + bp= nu->bp+u; + for(v=0; v<nu->pntsv-1; v++, bp+=nu->pntsu) { + if(v) if(bp->f1 & SELECT) return 1; + } + + return 0; +} + +typedef struct NurbSort { + struct NurbSort *next, *prev; + Nurb *nu; + float vec[3]; +} NurbSort; + +static ListBase nsortbase= {0, 0}; +/* static NurbSort *nusmain; */ /* this var seems to go unused... at least in this file */ + +static void make_selection_list_nurb(ListBase *editnurb) +{ + ListBase nbase= {0, 0}; + NurbSort *nus, *nustest, *headdo, *taildo; + Nurb *nu; + BPoint *bp; + float dist, headdist, taildist; + int a; + + for(nu= editnurb->first; nu; nu= nu->next) { + if( isNurbsel(nu) ) { + + nus = (NurbSort*)MEM_callocN(sizeof(NurbSort), "sort"); + BLI_addhead(&nbase, nus); + nus->nu= nu; + + bp= nu->bp; + a= nu->pntsu; + while(a--) { + VecAddf(nus->vec, nus->vec, bp->vec); + bp++; + } + VecMulf(nus->vec, 1.0/(float)nu->pntsu); + + + } + } + + /* just add the first one */ + nus= nbase.first; + BLI_remlink(&nbase, nus); + BLI_addtail( &nsortbase, nus); + + /* now add, either at head or tail, the closest one */ + while(nbase.first) { + + headdist= taildist= 1.0e30; + headdo= taildo= 0; + + nustest= nbase.first; + while(nustest) { + dist= VecLenf(nustest->vec, ((NurbSort *)nsortbase.first)->vec); + + if(dist<headdist) { + headdist= dist; + headdo= nustest; + } + dist= VecLenf(nustest->vec, ((NurbSort *)nsortbase.last)->vec); + + if(dist<taildist) { + taildist= dist; + taildo= nustest; + } + nustest= nustest->next; + } + + if(headdist<taildist) { + BLI_remlink(&nbase, headdo); + BLI_addhead(&nsortbase, headdo); + } + else { + BLI_remlink(&nbase, taildo); + BLI_addtail(&nsortbase, taildo); + } + } +} + +static void merge_2_nurb(wmOperator *op, ListBase *editnurb, Nurb *nu1, Nurb *nu2) +{ + BPoint *bp, *bp1, *bp2, *temp; + float len1, len2; + int origu, u, v; + + /* first nurbs will be changed to make u = resolu-1 selected */ + /* 2nd nurbs will be changed to make u = 0 selected */ + + /* first nurbs: u = resolu-1 selected */ + + if( is_u_selected(nu1, nu1->pntsu-1) ); + else { + rotate_direction_nurb(nu1); + if( is_u_selected(nu1, nu1->pntsu-1) ); + else { + rotate_direction_nurb(nu1); + if( is_u_selected(nu1, nu1->pntsu-1) ); + else { + rotate_direction_nurb(nu1); + if( is_u_selected(nu1, nu1->pntsu-1) ); + else { + /* rotate again, now its OK! */ + if(nu1->pntsv!=1) rotate_direction_nurb(nu1); + return; + } + } + } + } + + /* 2nd nurbs: u = 0 selected */ + if( is_u_selected(nu2, 0) ); + else { + rotate_direction_nurb(nu2); + if( is_u_selected(nu2, 0) ); + else { + rotate_direction_nurb(nu2); + if( is_u_selected(nu2, 0) ); + else { + rotate_direction_nurb(nu2); + if( is_u_selected(nu2, 0) ); + else { + /* rotate again, now its OK! */ + if(nu1->pntsu==1) rotate_direction_nurb(nu1); + if(nu2->pntsv!=1) rotate_direction_nurb(nu2); + return; + } + } + } + } + + if( nu1->pntsv != nu2->pntsv ) { + BKE_report(op->reports, RPT_ERROR, "Resolution doesn't match"); + return; + } + + /* ok, now nu1 has the rightmost collumn and nu2 the leftmost collumn selected */ + /* maybe we need a 'v' flip of nu2? */ + + bp1= nu1->bp+nu1->pntsu-1; + bp2= nu2->bp; + len1= 0.0; + + for(v=0; v<nu1->pntsv; v++, bp1+=nu1->pntsu, bp2+=nu2->pntsu) { + len1+= VecLenf(bp1->vec, bp2->vec); + } + + bp1= nu1->bp + nu1->pntsu-1; + bp2= nu2->bp + nu2->pntsu*(nu2->pntsv-1); + len2= 0.0; + + for(v=0; v<nu1->pntsv; v++, bp1+=nu1->pntsu, bp2-=nu2->pntsu) { + len2+= VecLenf(bp1->vec, bp2->vec); + } + + /* merge */ + origu= nu1->pntsu; + nu1->pntsu+= nu2->pntsu; + if(nu1->orderu<3) nu1->orderu++; + if(nu1->orderv<3) nu1->orderv++; + temp= nu1->bp; + nu1->bp= MEM_mallocN(nu1->pntsu*nu1->pntsv*sizeof(BPoint), "mergeBP"); + + bp= nu1->bp; + bp1= temp; + + for(v=0; v<nu1->pntsv; v++) { + + /* switch direction? */ + if(len1<len2) bp2= nu2->bp + v*nu2->pntsu; + else bp2= nu2->bp + (nu1->pntsv-v-1)*nu2->pntsu; + + for(u=0; u<nu1->pntsu; u++, bp++) { + if(u<origu) { + *bp= *bp1; bp1++; + select_bpoint(bp, SELECT, 1, HIDDEN); + } + else { + *bp= *bp2; bp2++; + } + } + } + + if((nu1->type & 7)==CU_NURBS) { + /* merge knots */ + makeknots(nu1, 1); + + /* make knots, for merged curved for example */ + makeknots(nu1, 2); + } + + MEM_freeN(temp); + BLI_remlink(editnurb, nu2); + freeNurb(nu2); +} + +static int merge_nurb(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + NurbSort *nus1, *nus2; + int ok= 1; + + make_selection_list_nurb(editnurb); + + if(nsortbase.first == nsortbase.last) { + BLI_freelistN(&nsortbase); + BKE_report(op->reports, RPT_ERROR, "Too few selections to merge."); + return OPERATOR_CANCELLED; + } + + nus1= nsortbase.first; + nus2= nus1->next; + + /* resolution match, to avoid uv rotations */ + if(nus1->nu->pntsv==1) { + if(nus1->nu->pntsu==nus2->nu->pntsu || nus1->nu->pntsu==nus2->nu->pntsv); + else ok= 0; + } + else if(nus2->nu->pntsv==1) { + if(nus2->nu->pntsu==nus1->nu->pntsu || nus2->nu->pntsu==nus1->nu->pntsv); + else ok= 0; + } + else if( nus1->nu->pntsu==nus2->nu->pntsu || nus1->nu->pntsv==nus2->nu->pntsv); + else if( nus1->nu->pntsu==nus2->nu->pntsv || nus1->nu->pntsv==nus2->nu->pntsu); + else { + ok= 0; + } + + if(ok==0) { + BKE_report(op->reports, RPT_ERROR, "Resolution doesn't match"); + BLI_freelistN(&nsortbase); + return OPERATOR_CANCELLED; + } + + while(nus2) { + merge_2_nurb(op, editnurb, nus1->nu, nus2->nu); + nus2= nus2->next; + } + + BLI_freelistN(&nsortbase); + + set_actNurb(obedit, NULL); + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +static int make_segment_exec(bContext *C, wmOperator *op) +{ + /* joins 2 curves */ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu, *nu1=0, *nu2=0; + BezTriple *bezt; + BPoint *bp; + float *fp, offset; + int a; + + /* first decide if this is a surface merge! */ + if(obedit->type==OB_SURF) nu= editnurb->first; + else nu= NULL; + + while(nu) { + if( isNurbsel(nu) ) { + + if(nu->pntsu>1 && nu->pntsv>1) break; + if(isNurbsel_count(nu)>1) break; + if(isNurbsel_count(nu)==1) { + /* only 1 selected, not first or last, a little complex, but intuitive */ + if(nu->pntsv==1) { + if( (nu->bp->f1 & SELECT) || ((nu->bp+nu->pntsu-1)->f1 & SELECT)); + else break; + } + } + } + nu= nu->next; + } + + if(nu) + return merge_nurb(C, op); + + /* find both nurbs and points, nu1 will be put behind nu2 */ + for(nu= editnurb->first; nu; nu= nu->next) { + if((nu->flagu & CU_CYCLIC)==0) { /* not cyclic */ + if( (nu->type & 7)==CU_BEZIER ) { + bezt= nu->bezt; + if(nu1==0) { + if( BEZSELECTED_HIDDENHANDLES(bezt) ) nu1= nu; + else { + bezt= bezt+(nu->pntsu-1); + if( BEZSELECTED_HIDDENHANDLES(bezt) ) { + nu1= nu; + switchdirectionNurb(nu); + } + } + } + else if(nu2==0) { + if( BEZSELECTED_HIDDENHANDLES(bezt) ) { + nu2= nu; + switchdirectionNurb(nu); + } + else { + bezt= bezt+(nu->pntsu-1); + if( BEZSELECTED_HIDDENHANDLES(bezt) ) { + nu2= nu; + } + } + } + else break; + } + else if(nu->pntsv==1) { + bp= nu->bp; + if(nu1==0) { + if( bp->f1 & SELECT) nu1= nu; + else { + bp= bp+(nu->pntsu-1); + if( bp->f1 & SELECT ) { + nu1= nu; + switchdirectionNurb(nu); + } + } + } + else if(nu2==0) { + if( bp->f1 & SELECT ) { + nu2= nu; + switchdirectionNurb(nu); + } + else { + bp= bp+(nu->pntsu-1); + if( bp->f1 & SELECT ) { + nu2= nu; + } + } + } + else break; + } + } + } + + if((nu1 && nu2) && (nu1!=nu2)) { + if( nu1->type==nu2->type) { + if((nu1->type & 7)==CU_BEZIER) { + bezt = + (BezTriple*)MEM_mallocN((nu1->pntsu+nu2->pntsu) * sizeof(BezTriple), "addsegmentN"); + memcpy(bezt, nu2->bezt, nu2->pntsu*sizeof(BezTriple)); + memcpy(bezt+nu2->pntsu, nu1->bezt, nu1->pntsu*sizeof(BezTriple)); + MEM_freeN(nu1->bezt); + nu1->bezt= bezt; + nu1->pntsu+= nu2->pntsu; + BLI_remlink(editnurb, nu2); + freeNurb(nu2); nu2= NULL; + calchandlesNurb(nu1); + } + else { + bp = + (BPoint*)MEM_mallocN((nu1->pntsu+nu2->pntsu) * sizeof(BPoint), "addsegmentN2"); + memcpy(bp, nu2->bp, nu2->pntsu*sizeof(BPoint) ); + memcpy(bp+nu2->pntsu, nu1->bp, nu1->pntsu*sizeof(BPoint)); + MEM_freeN(nu1->bp); + nu1->bp= bp; + + a= nu1->pntsu+nu1->orderu; + + nu1->pntsu+= nu2->pntsu; + BLI_remlink(editnurb, nu2); + + /* now join the knots */ + if((nu1->type & 7)==CU_NURBS) { + if(nu1->knotsu==NULL) { + makeknots(nu1, 1); + } + else { + fp= MEM_mallocN(sizeof(float)*KNOTSU(nu1), "addsegment3"); + memcpy(fp, nu1->knotsu, sizeof(float)*a); + MEM_freeN(nu1->knotsu); + nu1->knotsu= fp; + + + offset= nu1->knotsu[a-1] +1.0; + fp= nu1->knotsu+a; + for(a=0; a<nu2->pntsu; a++, fp++) { + if(nu2->knotsu) + *fp= offset+nu2->knotsu[a+1]; + else + *fp = offset; + } + } + } + freeNurb(nu2); nu2= NULL; + } + } + + set_actNurb(obedit, NULL); /* for selected */ + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + return OPERATOR_FINISHED; + } + else { + BKE_report(op->reports, RPT_ERROR, "Can't make segment"); + return OPERATOR_CANCELLED; + } +} + +void CURVE_OT_make_segment(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Make Segment"; + ot->idname= "CURVE_OT_make_segment"; + + /* api callbacks */ + ot->exec= make_segment_exec; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/***************** pick select from 3d view **********************/ + +void mouse_nurb(bContext *C, short mval[2], int extend) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Curve *cu= obedit->data; + ViewContext vc; + Nurb *nu; + BezTriple *bezt=0; + BPoint *bp=0; + int location[2]; + short hand; + + view3d_set_viewcontext(C, &vc); + + location[0]= mval[0]; + location[1]= mval[1]; + hand= findnearestNurbvert(&vc, 1, location, &nu, &bezt, &bp); + + if(bezt || bp) { + if(extend==0) { + + setflagsNurb(editnurb, 0); + + if(bezt) { + + if(hand==1) select_beztriple(bezt, SELECT, 1, HIDDEN); + else if(hand==0) bezt->f1|= SELECT; + else bezt->f3|= SELECT; + } + else { + cu->lastselbp= bp; + select_bpoint(bp, SELECT, 1, HIDDEN); + } + + } + else { + if(bezt) { + if(hand==1) { + if(bezt->f2 & SELECT) select_beztriple(bezt, DESELECT, 1, HIDDEN); + else select_beztriple(bezt, SELECT, 1, HIDDEN); + } else if(hand==0) { + bezt->f1 ^= SELECT; + } else { + bezt->f3 ^= SELECT; + } + } + else { + if(bp->f1 & SELECT) select_bpoint(bp, DESELECT, 1, HIDDEN); + else { + select_bpoint(bp, SELECT, 1, HIDDEN); + cu->lastselbp= bp; + } + } + + } + + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + if(nu!=get_actNurb(obedit)) + set_actNurb(obedit, nu); +} + +/******************** spin operator ***********************/ + +/* from what I can gather, the mode==0 magic number spins and bridges the nurbs based on the + * orientation of the global 3d view (yuck yuck!) mode==1 does the same, but doesn't bridge up + * up the new geometry, mode==2 now does the same as 0, but aligned to world axes, not the view. +*/ +static int spin_nurb(bContext *C, Scene *scene, Object *obedit, float *dvec, short mode) +{ + ListBase *editnurb= curve_get_editcurve(obedit); + View3D *v3d= CTX_wm_view3d(C); + RegionView3D *rv3d= CTX_wm_region_view3d(C); + Nurb *nu; + float *curs, si,phi,n[3],q[4],cmat[3][3],tmat[3][3],imat[3][3]; + float cent[3],bmat[3][3], rotmat[3][3], scalemat1[3][3], scalemat2[3][3]; + float persmat[3][3], persinv[3][3]; + short a,ok, changed= 0; + + if(mode != 2 && rv3d) Mat3CpyMat4(persmat, rv3d->viewmat); + else Mat3One(persmat); + Mat3Inv(persinv, persmat); + + /* imat and center and size */ + Mat3CpyMat4(bmat, obedit->obmat); + Mat3Inv(imat, bmat); + + if(v3d) { + curs= give_cursor(scene, v3d); + VECCOPY(cent, curs); + } + else + cent[0]= cent[1]= cent[2]= 0.0f; + + VecSubf(cent, cent, obedit->obmat[3]); + Mat3MulVecfl(imat,cent); + + if(dvec || mode==2 || !rv3d) { + n[0]=n[1]= 0.0; + n[2]= 1.0; + } else { + n[0]= rv3d->viewinv[2][0]; + n[1]= rv3d->viewinv[2][1]; + n[2]= rv3d->viewinv[2][2]; + Normalize(n); + } + + phi= M_PI/8.0; + q[0]= cos(phi); + si= sin(phi); + q[1]= n[0]*si; + q[2]= n[1]*si; + q[3]= n[2]*si; + QuatToMat3(q, cmat); + Mat3MulMat3(tmat, cmat, bmat); + Mat3MulMat3(rotmat, imat, tmat); + + Mat3One(scalemat1); + scalemat1[0][0]= sqrt(2.0); + scalemat1[1][1]= sqrt(2.0); + + Mat3MulMat3(tmat,persmat,bmat); + Mat3MulMat3(cmat,scalemat1,tmat); + Mat3MulMat3(tmat,persinv,cmat); + Mat3MulMat3(scalemat1,imat,tmat); + + Mat3One(scalemat2); + scalemat2[0][0]/= sqrt(2.0); + scalemat2[1][1]/= sqrt(2.0); + + Mat3MulMat3(tmat,persmat,bmat); + Mat3MulMat3(cmat,scalemat2,tmat); + Mat3MulMat3(tmat,persinv,cmat); + Mat3MulMat3(scalemat2,imat,tmat); + + ok= 1; + + for(a=0;a<7;a++) { + if(mode==0 || mode==2) ok= extrudeflagNurb(editnurb, 1); + else adduplicateflagNurb(obedit, 1); + + if(ok==0) + return changed; + + changed= 1; + + rotateflagNurb(editnurb, 1,cent,rotmat); + + if(mode==0 || mode==2) { + if( (a & 1)==0 ) { + rotateflagNurb(editnurb, 1,cent,scalemat1); + weightflagNurb(editnurb, 1, 0.25*sqrt(2.0), 1); + } + else { + rotateflagNurb(editnurb, 1,cent,scalemat2); + weightflagNurb(editnurb, 1, 4.0/sqrt(2.0), 1); + } + } + if(dvec) { + Mat3MulVecfl(bmat,dvec); + translateflagNurb(editnurb, 1,dvec); + } + } + + if(ok) { + for(nu= editnurb->first; nu; nu= nu->next) { + if(isNurbsel(nu)) { + nu->orderv= 4; + nu->flagv |= CU_CYCLIC; + makeknots(nu, 2); + } + } + } + + return changed; +} + +static int spin_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + + if(!spin_nurb(C, scene, obedit, 0, 0)) { + BKE_report(op->reports, RPT_ERROR, "Can't spin"); + return OPERATOR_CANCELLED; + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_spin(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Spin"; + ot->idname= "CURVE_OT_spin"; + + /* api callbacks */ + ot->exec= spin_exec; + ot->poll= ED_operator_editsurf; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/***************** add vertex operator **********************/ + +static int addvert_Nurb(bContext *C, short mode, float location[3]) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BezTriple *bezt, *newbezt = NULL; + BPoint *bp, *newbp = NULL; + float mat[3][3],imat[3][3], temp[3]; + + Mat3CpyMat4(mat, obedit->obmat); + Mat3Inv(imat,mat); + + findselectedNurbvert(editnurb, &nu, &bezt, &bp); + if(bezt==0 && bp==0) return OPERATOR_CANCELLED; + + if((nu->type & 7)==CU_BEZIER) { + /* which bezpoint? */ + if(bezt== nu->bezt) { /* first */ + BEZ_DESEL(bezt); + newbezt = + (BezTriple*)MEM_callocN((nu->pntsu+1) * sizeof(BezTriple), "addvert_Nurb"); + memcpy(newbezt+1, bezt, nu->pntsu*sizeof(BezTriple)); + *newbezt= *bezt; + BEZ_SEL(newbezt); + newbezt->h2= newbezt->h1; + VECCOPY(temp, bezt->vec[1]); + MEM_freeN(nu->bezt); + nu->bezt= newbezt; + bezt= newbezt+1; + } + else if(bezt== (nu->bezt+nu->pntsu-1)) { /* last */ + BEZ_DESEL(bezt); + newbezt = + (BezTriple*)MEM_callocN((nu->pntsu+1) * sizeof(BezTriple), "addvert_Nurb"); + memcpy(newbezt, nu->bezt, nu->pntsu*sizeof(BezTriple)); + *(newbezt+nu->pntsu)= *bezt; + VECCOPY(temp, bezt->vec[1]); + MEM_freeN(nu->bezt); + nu->bezt= newbezt; + newbezt+= nu->pntsu; + BEZ_SEL(newbezt); + newbezt->h2= newbezt->h1; + bezt= nu->bezt+nu->pntsu-1; + } + else bezt= 0; + + if(bezt) { + nu->pntsu++; + + if(mode=='e') { + VECCOPY(newbezt->vec[0], bezt->vec[0]); + VECCOPY(newbezt->vec[1], bezt->vec[1]); + VECCOPY(newbezt->vec[2], bezt->vec[2]); + } + else { + VECCOPY(newbezt->vec[1], location); + VecSubf(newbezt->vec[1],newbezt->vec[1], obedit->obmat[3]); + Mat3MulVecfl(imat,newbezt->vec[1]); + VecSubf(temp, newbezt->vec[1],temp); + VecAddf(newbezt->vec[0], bezt->vec[0],temp); + VecAddf(newbezt->vec[2], bezt->vec[2],temp); + calchandlesNurb(nu); + } + } + } + else if(nu->pntsv==1) { + /* which b-point? */ + if(bp== nu->bp) { /* first */ + bp->f1= 0; + newbp = + (BPoint*)MEM_callocN((nu->pntsu+1) * sizeof(BPoint), "addvert_Nurb3"); + memcpy(newbp+1, bp, nu->pntsu*sizeof(BPoint)); + *newbp= *bp; + newbp->f1= 1; + MEM_freeN(nu->bp); + nu->bp= newbp; + bp= newbp + 1; + } + else if(bp== (nu->bp+nu->pntsu-1)) { /* last */ + bp->f1= 0; + newbp = + (BPoint*)MEM_callocN((nu->pntsu+1) * sizeof(BPoint), "addvert_Nurb4"); + memcpy(newbp, nu->bp, nu->pntsu*sizeof(BPoint)); + *(newbp+nu->pntsu)= *bp; + MEM_freeN(nu->bp); + nu->bp= newbp; + newbp+= nu->pntsu; + newbp->f1= 1; + bp= newbp - 1; + } + else bp= 0; + + if(bp) { + nu->pntsu++; + + makeknots(nu, 1); + + if(mode=='e') { + VECCOPY(newbp->vec, bp->vec); + } + else { + VECCOPY(newbp->vec, location); + VecSubf(newbp->vec, newbp->vec, obedit->obmat[3]); + Mat3MulVecfl(imat,newbp->vec); + newbp->vec[3]= 1.0; + } + } + } + + // XXX retopo_do_all(); + + test2DNurb(nu); + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +static int add_vertex_exec(bContext *C, wmOperator *op) +{ + float location[3]; + + RNA_float_get_array(op->ptr, "location", location); + return addvert_Nurb(C, 0, location); +} + +static int add_vertex_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + RegionView3D *rv3d= CTX_wm_region_view3d(C); + ViewContext vc; + float location[3]; + short mval[2]; + + if(rv3d && !RNA_property_is_set(op->ptr, "location")) { + view3d_set_viewcontext(C, &vc); + + mval[0]= event->x - vc.ar->winrct.xmin; + mval[1]= event->y - vc.ar->winrct.ymin; + + view3d_get_view_aligned_coordinate(&vc, location, mval); + RNA_float_set_array(op->ptr, "location", location); + } + + return add_vertex_exec(C, op); +} + +void CURVE_OT_vertex_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Add Vertex"; + ot->idname= "CURVE_OT_vertex_add"; + + /* api callbacks */ + ot->exec= add_vertex_exec; + ot->invoke= add_vertex_invoke; + ot->poll= ED_operator_editcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_float_vector(ot->srna, "location", 3, NULL, -FLT_MAX, FLT_MAX, "Location", "Location to add new vertex at.", -1e4, 1e4); +} + +/***************** extrude operator **********************/ + +static int extrude_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + + /* first test: curve? */ + for(nu= editnurb->first; nu; nu= nu->next) + if(nu->pntsv==1 && isNurbsel_count(nu)==1) + break; + + if(obedit->type==OB_CURVE || nu) { + addvert_Nurb(C, 'e', NULL); + } + else { + if(extrudeflagNurb(editnurb, 1)) { /* '1'= flag */ + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + } + } + + return OPERATOR_FINISHED; +} + +static int extrude_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + if(extrude_exec(C, op) == OPERATOR_FINISHED) { + RNA_int_set(op->ptr, "mode", TFM_TRANSLATION); + WM_operator_name_call(C, "TFM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr); + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void CURVE_OT_extrude(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Extrude"; + ot->idname= "CURVE_OT_extrude"; + + /* api callbacks */ + ot->exec= extrude_exec; + ot->invoke= extrude_invoke; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* to give to transform */ + RNA_def_int(ot->srna, "mode", TFM_TRANSLATION, 0, INT_MAX, "Mode", "", 0, INT_MAX); +} + +/***************** make cyclic operator **********************/ + +static int toggle_cyclic_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + int a, direction= RNA_enum_get(op->ptr, "direction"); + + for(nu= editnurb->first; nu; nu= nu->next) { + if( nu->pntsu>1 || nu->pntsv>1) { + if( (nu->type & 7)==CU_POLY ) { + a= nu->pntsu; + bp= nu->bp; + while(a--) { + if( bp->f1 & SELECT ) { + nu->flagu ^= CU_CYCLIC; + break; + } + bp++; + } + } + else if( (nu->type & 7)==CU_BEZIER ) { + a= nu->pntsu; + bezt= nu->bezt; + while(a--) { + if( BEZSELECTED_HIDDENHANDLES(bezt) ) { + nu->flagu ^= CU_CYCLIC; + break; + } + bezt++; + } + calchandlesNurb(nu); + } + else if(nu->pntsv==1 && (nu->type & 7)==CU_NURBS) { + if (nu->knotsu) { /* if check_valid_nurb_u fails the knotsu can be NULL */ + a= nu->pntsu; + bp= nu->bp; + while(a--) { + if( bp->f1 & SELECT ) { + nu->flagu ^= CU_CYCLIC; + makeknots(nu, 1); /* 1==u type is ignored for cyclic curves */ + break; + } + bp++; + } + } + } + else if(nu->type==CU_NURBS) { + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a--) { + + if( bp->f1 & SELECT) { + if(direction==0 && nu->pntsu>1) { + nu->flagu ^= CU_CYCLIC; + makeknots(nu, 1); /* 1==u type is ignored for cyclic curves */ + } + if(direction==1 && nu->pntsv>1) { + nu->flagv ^= CU_CYCLIC; + makeknots(nu, 2); /* 2==v type is ignored for cyclic curves */ + } + break; + } + bp++; + } + + } + } + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +static int toggle_cyclic_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + uiPopupMenu *pup; + uiLayout *layout; + Nurb *nu; + + for(nu= editnurb->first; nu; nu= nu->next) { + if(nu->pntsu>1 || nu->pntsv>1) { + if(nu->type==CU_NURBS) { + pup= uiPupMenuBegin(C, "Direction", 0); + layout= uiPupMenuLayout(pup); + uiItemsEnumO(layout, op->type->idname, "direction"); + uiPupMenuEnd(C, pup); + return OPERATOR_CANCELLED; + } + } + } + + return toggle_cyclic_exec(C, op); +} + +void CURVE_OT_cyclic_toggle(wmOperatorType *ot) +{ + static EnumPropertyItem direction_items[]= { + {0, "CYCLIC_U", "Cyclic U", ""}, + {1, "CYCLIC_V", "Cyclic V", ""}, + {0, NULL, NULL, NULL}}; + + /* identifiers */ + ot->name= "Toggle Cyclic"; + ot->idname= "CURVE_OT_cyclic_toggle"; + + /* api callbacks */ + ot->exec= toggle_cyclic_exec; + ot->invoke= toggle_cyclic_invoke; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, "direction", direction_items, 0, "Direction", "Direction to make surface cyclic in."); +} + +/***************** select linked operator ******************/ + +static int select_linked_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + RegionView3D *rv3d= CTX_wm_region_view3d(C); + ViewContext vc; + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + int a, location[2], deselect; + + if(!rv3d) + return OPERATOR_CANCELLED; + + deselect= RNA_boolean_get(op->ptr, "deselect"); + RNA_int_get_array(op->ptr, "location", location); + + view3d_set_viewcontext(C, &vc); + findnearestNurbvert(&vc, 1, location, &nu, &bezt, &bp); + + if(bezt) { + a= nu->pntsu; + bezt= nu->bezt; + while(a--) { + if(deselect) select_beztriple(bezt, DESELECT, 1, VISIBLE); + else select_beztriple(bezt, SELECT, 1, VISIBLE); + bezt++; + } + } + else if(bp) { + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a--) { + if(deselect) select_bpoint(bp, DESELECT, 1, VISIBLE); + else select_bpoint(bp, SELECT, 1, VISIBLE); + bp++; + } + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +static int select_linked_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + ARegion *ar= CTX_wm_region(C); + int location[2]; + + location[0]= event->x - ar->winrct.xmin; + location[1]= event->y - ar->winrct.ymin; + RNA_int_set_array(op->ptr, "location", location); + + return select_linked_exec(C, op); +} + +void CURVE_OT_select_linked(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select Linked"; + ot->idname= "CURVE_OT_select_linked"; + + /* api callbacks */ + ot->exec= select_linked_exec; + ot->invoke= select_linked_invoke; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect linked control points rather than selecting them."); + RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "", 0, 16384); +} + +/***************** select row operator **********************/ + +static int select_row_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Curve *cu= obedit->data; + static BPoint *last=0; + static int direction=0; + Nurb *nu; + BPoint *bp; + int u = 0, v = 0, a, b, ok=0; + + if(editnurb->first==0) + return OPERATOR_CANCELLED; + if(cu->lastselbp==NULL) + return OPERATOR_CANCELLED; + + /* find the correct nurb and toggle with u of v */ + for(nu= editnurb->first; nu; nu= nu->next) { + bp= nu->bp; + for(v=0; v<nu->pntsv; v++) { + for(u=0; u<nu->pntsu; u++, bp++) { + if(bp==cu->lastselbp) { + if(bp->f1 & SELECT) { + ok= 1; + break; + } + } + } + if(ok) break; + } + + if(ok) { + if(last==cu->lastselbp) { + direction= 1-direction; + setflagsNurb(editnurb, 0); + } + last= cu->lastselbp; + + bp= nu->bp; + for(a=0; a<nu->pntsv; a++) { + for(b=0; b<nu->pntsu; b++, bp++) { + if(direction) { + if(a==v) select_bpoint(bp, SELECT, 1, VISIBLE); + } + else { + if(b==u) select_bpoint(bp, SELECT, 1, VISIBLE); + } + } + } + + break; + } + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_select_row(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select Control Point Row"; + ot->idname= "CURVE_OT_select_row"; + + /* api callbacks */ + ot->exec= select_row_exec; + ot->poll= ED_operator_editsurf; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/***************** select next operator **********************/ + +static int select_next_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + + select_adjacent_cp(editnurb, 1, 0, SELECT); + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_select_next(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select Next"; + ot->idname= "CURVE_OT_select_next"; + + /* api callbacks */ + ot->exec= select_next_exec; + ot->poll= ED_operator_editcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/***************** select previous operator **********************/ + +static int select_previous_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + + select_adjacent_cp(editnurb, -1, 0, SELECT); + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_select_previous(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select Previous"; + ot->idname= "CURVE_OT_select_previous"; + + /* api callbacks */ + ot->exec= select_previous_exec; + ot->poll= ED_operator_editcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/***************** select more operator **********************/ + +static int select_more_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BPoint *bp, *tempbp; + int a; + short sel= 0; + short *selbpoints; + + /* note that NURBS surface is a special case because we mimic */ + /* the behaviour of "select more" of mesh tools. */ + /* The algorithm is designed to work in planar cases so it */ + /* may not be optimal always (example: end of NURBS sphere) */ + if(obedit->type==OB_SURF) { + for(nu= editnurb->first; nu; nu= nu->next) { + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + selbpoints= MEM_callocN(sizeof(short)*a-nu->pntsu, "selectlist"); + while(a > 0) { + if((selbpoints[a]!=1) && (bp->hide==0) && (bp->f1 & SELECT)) { + /* upper control point */ + if(a%nu->pntsu != 0) { + tempbp= bp-1; + if(!(tempbp->f1 & SELECT)) select_bpoint(tempbp, SELECT, 1, VISIBLE); + } + + /* left control point. select only if it is not selected already */ + if(a-nu->pntsu > 0) { + sel= 0; + tempbp= bp+nu->pntsu; + if(!(tempbp->f1 & SELECT)) sel= select_bpoint(tempbp, SELECT, 1, VISIBLE); + /* make sure selected bpoint is discarded */ + if(sel == 1) selbpoints[a-nu->pntsu]= 1; + } + + /* right control point */ + if(a+nu->pntsu < nu->pntsu*nu->pntsv) { + tempbp= bp-nu->pntsu; + if(!(tempbp->f1 & SELECT)) select_bpoint(tempbp, SELECT, 1, VISIBLE); + } + + /* lower control point. skip next bp in case selection was made */ + if(a%nu->pntsu != 1) { + sel= 0; + tempbp= bp+1; + if(!(tempbp->f1 & 1)) sel= select_bpoint(tempbp, SELECT, 1, VISIBLE); + if(sel) { + bp++; + a--; + } + } + } + + bp++; + a--; + } + + MEM_freeN(selbpoints); + } + } + else { + select_adjacent_cp(editnurb, 1, 0, SELECT); + select_adjacent_cp(editnurb, -1, 0, SELECT); + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_select_more(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select More"; + ot->idname= "CURVE_OT_select_more"; + + /* api callbacks */ + ot->exec= select_more_exec; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/******************** select less operator *****************/ + +/* basic method: deselect if control point doesn't have all neighbours selected */ +static int select_less_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BPoint *bp; + BezTriple *bezt; + int a; + short sel= 0, lastsel= 0; + short *selbpoints; + + if(obedit->type==OB_SURF) { + for(nu= editnurb->first; nu; nu= nu->next) { + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + selbpoints= MEM_callocN(sizeof(short)*a, "selectlist"); + while(a--) { + if((bp->hide==0) && (bp->f1 & SELECT)) { + sel= 0; + + /* check if neighbours have been selected */ + /* edges of surface are an exception */ + if((a+1)%nu->pntsu==0) sel++; + else { + bp--; + if((selbpoints[a+1]==1) || ((bp->hide==0) && (bp->f1 & SELECT))) sel++; + bp++; + } + + if((a+1)%nu->pntsu==1) sel++; + else { + bp++; + if((bp->hide==0) && (bp->f1 & SELECT)) sel++; + bp--; + } + + if(a+1 > nu->pntsu*nu->pntsv-nu->pntsu) sel++; + else { + bp-=nu->pntsu; + if((selbpoints[a+nu->pntsu]==1) || ((bp->hide==0) && (bp->f1 & SELECT))) sel++; + bp+=nu->pntsu; + } + + if(a < nu->pntsu) sel++; + else { + bp+=nu->pntsu; + if((bp->hide==0) && (bp->f1 & SELECT)) sel++; + bp-=nu->pntsu; + } + + if(sel!=4) { + select_bpoint(bp, DESELECT, 1, VISIBLE); + selbpoints[a]= 1; + } + } + else lastsel= 0; + + bp++; + } + + MEM_freeN(selbpoints); + } + } + else { + for(nu= editnurb->first; nu; nu= nu->next) { + lastsel=0; + /* check what type of curve/nurb it is */ + if((nu->type & 7)==CU_BEZIER) { + a= nu->pntsu; + bezt= nu->bezt; + while(a--) { + if((bezt->hide==0) && (bezt->f2 & SELECT)) { + if(lastsel==1) sel= 1; + else sel= 0; + + /* check if neighbours have been selected */ + /* first and last are exceptions */ + if(a==nu->pntsu-1) sel++; + else { + bezt--; + if((bezt->hide==0) && (bezt->f2 & SELECT)) sel++; + bezt++; + } + + if(a==0) sel++; + else { + bezt++; + if((bezt->hide==0) && (bezt->f2 & SELECT)) sel++; + bezt--; + } + + if(sel!=2) { + select_beztriple(bezt, DESELECT, 1, VISIBLE); + lastsel= 1; + } + else lastsel= 0; + } + else lastsel= 0; + + bezt++; + } + } + else { + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a--) { + if((lastsel==0) && (bp->hide==0) && (bp->f1 & SELECT)) { + if(lastsel!=0) sel= 1; + else sel= 0; + + /* first and last are exceptions */ + if(a==nu->pntsu*nu->pntsv-1) sel++; + else { + bp--; + if((bp->hide==0) && (bp->f1 & SELECT)) sel++; + bp++; + } + + if(a==0) sel++; + else { + bp++; + if((bp->hide==0) && (bp->f1 & SELECT)) sel++; + bp--; + } + + if(sel!=2) { + select_bpoint(bp, DESELECT, 1, VISIBLE); + lastsel= 1; + } + else lastsel= 0; + } + else lastsel= 0; + + bp++; + } + } + } + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_select_less(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select Less"; + ot->idname= "CURVE_OT_select_less"; + + /* api callbacks */ + ot->exec= select_less_exec; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/********************** select random *********************/ + +/* this function could be moved elsewhere as it can be reused in other parts of the source needing randomized list */ +/* returns list containing -1 in indices that have been left out of the list. otherwise index contains reference */ +/* to next index. basically *list contains a linked list */ +static void generate_pickable_list(int *list, int size, int pickamount) +{ + int i, j, removable; + + BLI_srand( BLI_rand() ); /* random seed */ + + /* generate list in form 0->1, 1->2, 2->3, ... i-2->i-1, i->0 */ + for(i=0; i<size; i++) { + if(i == size-1) list[i]= 0; + else list[i]= i+1; + } + + for(i=0; i<size-pickamount; i++) { + removable= floor(BLI_frand()*(size-1)+0.5); /* with rounding. frand returns [0,1] */ + + /* seek proper item as the one randomly selected might not be appropriate */ + for(j=0; j<size; j++, removable++) { + if(list[removable] != -1) break; + if(removable == size-1) removable= -1; + } + + /* pick unwanted item out of the list */ + list[list[removable]]= -1; /* mark former last as invalid */ + + if(list[removable] == size-1) list[removable]= 0; + else list[removable]= list[removable]+1; + } +} + +static int select_random_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + float percent= RNA_float_get(op->ptr, "percent"); + int amounttoselect, amountofcps, a, i, k= 0; + int *itemstobeselected; + + if(percent == 0.0f) + return OPERATOR_CANCELLED; + + amountofcps= count_curveverts_without_handles(editnurb); + itemstobeselected= MEM_callocN(sizeof(int) * amountofcps, "selectitems"); + amounttoselect= floor(percent * amountofcps + 0.5); + generate_pickable_list(itemstobeselected, amountofcps, amounttoselect); + + /* select elements */ + for(i=1, nu= editnurb->first; nu; nu= nu->next) { + if((nu->type & 7)==CU_BEZIER) { + bezt= nu->bezt; + a= nu->pntsu; + while(a--) { + if(itemstobeselected[k] != -1) select_beztriple(bezt, SELECT, 1, VISIBLE); + k++; + bezt++; + } + } + else { + bp= nu->bp; + a= nu->pntsu*nu->pntsv; + while(a--) { + if(itemstobeselected[k] != -1) select_bpoint(bp, SELECT, 1, VISIBLE); + k++; + bp++; + } + } + } + + MEM_freeN(itemstobeselected); + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_select_random(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select Random"; + ot->idname= "CURVE_OT_select_random"; + + /* api callbacks */ + ot->exec= select_random_exec; + ot->invoke= WM_operator_redo; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_float_percentage(ot->srna, "percent", 0.5f, 0.0f, 1.0f, "Percent", "Percentage of vertices to select randomly.", 0.0001f, 1.0f); +} + +/********************** select every nth *********************/ + +static int select_every_nth_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + int n= RNA_int_get(op->ptr, "n"); + + select_adjacent_cp(editnurb, n, 1, SELECT); + select_adjacent_cp(editnurb, -n, 1, SELECT); + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_select_every_nth(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select Every Nth"; + ot->idname= "CURVE_OT_select_every_nth"; + + /* api callbacks */ + ot->exec= select_every_nth_exec; + ot->invoke= WM_operator_redo; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_int(ot->srna, "n", 2, 2, INT_MAX, "N", "Select every Nth element", 2, 25); +} + +/********************** add duplicate operator *********************/ + +static int duplicate_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + + adduplicateflagNurb(obedit, 1); + + return OPERATOR_FINISHED; +} + +static int duplicate_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + duplicate_exec(C, op); + + RNA_int_set(op->ptr, "mode", TFM_TRANSLATION); + WM_operator_name_call(C, "TFM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_duplicate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Duplicate"; + ot->idname= "CURVE_OT_duplicate"; + + /* api callbacks */ + ot->exec= duplicate_exec; + ot->invoke= duplicate_invoke; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* to give to transform */ + RNA_def_int(ot->srna, "mode", TFM_TRANSLATION, 0, INT_MAX, "Mode", "", 0, INT_MAX); +} + +/********************** delete operator *********************/ + +static int delete_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu, *next, *nu1; + BezTriple *bezt, *bezt1, *bezt2; + BPoint *bp, *bp1, *bp2; + int a, cut= 0, type= RNA_enum_get(op->ptr, "type"); + + if(obedit->type==OB_SURF) { + if(type==0) deleteflagNurb(C, op, 1); + else freeNurblist(editnurb); + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + return OPERATOR_FINISHED; + } + + if(type==0) { + /* first loop, can we remove entire pieces? */ + nu= editnurb->first; + while(nu) { + next= nu->next; + if( (nu->type & 7)==CU_BEZIER ) { + bezt= nu->bezt; + a= nu->pntsu; + if(a) { + while(a) { + if( BEZSELECTED_HIDDENHANDLES(bezt) ); + else break; + a--; + bezt++; + } + if(a==0) { + BLI_remlink(editnurb, nu); + freeNurb(nu); nu= NULL; + } + } + } + else { + bp= nu->bp; + a= nu->pntsu*nu->pntsv; + if(a) { + while(a) { + if(bp->f1 & SELECT); + else break; + a--; + bp++; + } + if(a==0) { + BLI_remlink(editnurb, nu); + freeNurb(nu); nu= NULL; + } + } + } + + /* Never allow the order to exceed the number of points + - note, this is ok but changes unselected nurbs, disable for now */ + /* + if ((nu!= NULL) && ((nu->type & 7)==CU_NURBS)) { + clamp_nurb_order_u(nu); + } + */ + nu= next; + } + /* 2nd loop, delete small pieces: just for curves */ + nu= editnurb->first; + while(nu) { + next= nu->next; + type= 0; + if( (nu->type & 7)==CU_BEZIER ) { + bezt= nu->bezt; + for(a=0;a<nu->pntsu;a++) { + if( BEZSELECTED_HIDDENHANDLES(bezt) ) { + memmove(bezt, bezt+1, (nu->pntsu-a-1)*sizeof(BezTriple)); + nu->pntsu--; + a--; + type= 1; + } + else bezt++; + } + if(type) { + bezt1 = + (BezTriple*)MEM_mallocN((nu->pntsu) * sizeof(BezTriple), "delNurb"); + memcpy(bezt1, nu->bezt, (nu->pntsu)*sizeof(BezTriple) ); + MEM_freeN(nu->bezt); + nu->bezt= bezt1; + calchandlesNurb(nu); + } + } + else if(nu->pntsv==1) { + bp= nu->bp; + + for(a=0;a<nu->pntsu;a++) { + if( bp->f1 & SELECT ) { + memmove(bp, bp+1, (nu->pntsu-a-1)*sizeof(BPoint)); + nu->pntsu--; + a--; + type= 1; + } + else { + bp++; + } + } + if(type) { + bp1 = (BPoint*)MEM_mallocN(nu->pntsu * sizeof(BPoint), "delNurb2"); + memcpy(bp1, nu->bp, (nu->pntsu)*sizeof(BPoint) ); + MEM_freeN(nu->bp); + nu->bp= bp1; + + /* Never allow the order to exceed the number of points\ + - note, this is ok but changes unselected nurbs, disable for now */ + /* + if ((nu->type & 7)==CU_NURBS) { + clamp_nurb_order_u(nu); + }*/ + } + makeknots(nu, 1); + } + nu= next; + } + } + else if(type==1) { /* erase segment */ + /* find the 2 selected points */ + bezt1= bezt2= 0; + bp1= bp2= 0; + nu= editnurb->first; + nu1= 0; + while(nu) { + next= nu->next; + if( (nu->type & 7)==CU_BEZIER ) { + bezt= nu->bezt; + for(a=0; a<nu->pntsu-1; a++) { + if( BEZSELECTED_HIDDENHANDLES(bezt) ) { + bezt1= bezt; + bezt2= bezt+1; + if( (bezt2->f1 & SELECT) || (bezt2->f2 & SELECT) || (bezt2->f3 & SELECT) ) ; + else { /* maybe do not make cyclic */ + if(a==0 && (nu->flagu & CU_CYCLIC) ) { + bezt2= bezt+(nu->pntsu-1); + if( (bezt2->f1 & SELECT) || (bezt2->f2 & SELECT) || (bezt2->f3 & SELECT) ) { + nu->flagu &= ~CU_CYCLIC; + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + } + } + + return OPERATOR_FINISHED; + } + cut= a; + nu1= nu; + break; + } + bezt++; + } + } + else if(nu->pntsv==1) { + bp= nu->bp; + for(a=0; a<nu->pntsu-1; a++) { + if( bp->f1 & SELECT ) { + bp1= bp; + bp2= bp+1; + if( bp2->f1 & 1 ) ; + else { /* maybe do not make cyclic */ + if(a==0 && (nu->flagu & CU_CYCLIC) ) { + bp2= bp+(nu->pntsu-1); + if( bp2->f1 & SELECT ) { + nu->flagu &= ~CU_CYCLIC; + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + } + } + + return OPERATOR_FINISHED; + } + cut= a; + nu1= nu; + break; + } + bp++; + } + } + if(nu1) break; + + nu= nu->next; + } + if(nu1) { + if(bezt1) { + if(nu1->pntsu==2) { /* remove completely */ + BLI_remlink(editnurb, nu); + freeNurb(nu); nu = NULL; + } + else if(nu1->flagu & CU_CYCLIC) { /* cyclic */ + bezt = + (BezTriple*)MEM_mallocN((cut+1) * sizeof(BezTriple), "delNurb1"); + memcpy(bezt, nu1->bezt,(cut+1)*sizeof(BezTriple)); + a= nu1->pntsu-cut-1; + memcpy(nu1->bezt, bezt2, a*sizeof(BezTriple)); + memcpy(nu1->bezt+a, bezt, (cut+1)*sizeof(BezTriple)); + nu1->flagu &= ~CU_CYCLIC; + MEM_freeN(bezt); + calchandlesNurb(nu); + } + else { /* add new curve */ + +/* seems to be an error here... but where? (a can become zero) */ + + nu = + (Nurb*)MEM_mallocN(sizeof(Nurb), "delNurb2"); + memcpy(nu, nu1, sizeof(Nurb)); + BLI_addtail(editnurb, nu); + nu->bezt = + (BezTriple*)MEM_mallocN((cut+1) * sizeof(BezTriple), "delNurb3"); + memcpy(nu->bezt, nu1->bezt,(cut+1)*sizeof(BezTriple)); + a= nu1->pntsu-cut-1; + + bezt = + (BezTriple*)MEM_mallocN(a * sizeof(BezTriple), "delNurb4"); + memcpy(bezt, nu1->bezt+cut+1,a*sizeof(BezTriple)); + MEM_freeN(nu1->bezt); + nu1->bezt= bezt; + nu1->pntsu= a; + nu->pntsu= cut+1; + + + calchandlesNurb(nu); + calchandlesNurb(nu1); + } + } + else if(bp1) { + if(nu1->pntsu==2) { /* remove completely */ + BLI_remlink(editnurb, nu); + freeNurb(nu); nu= NULL; + } + else if(nu1->flagu & CU_CYCLIC) { /* cyclic */ + bp = + (BPoint*)MEM_mallocN((cut+1) * sizeof(BPoint), "delNurb5"); + memcpy(bp, nu1->bp,(cut+1)*sizeof(BPoint)); + a= nu1->pntsu-cut-1; + memcpy(nu1->bp, bp2, a*sizeof(BPoint)); + memcpy(nu1->bp+a, bp, (cut+1)*sizeof(BPoint)); + nu1->flagu &= ~CU_CYCLIC; + MEM_freeN(bp); + } + else { /* add new curve */ + nu = (Nurb*)MEM_mallocN(sizeof(Nurb), "delNurb6"); + memcpy(nu, nu1, sizeof(Nurb)); + BLI_addtail(editnurb, nu); + nu->bp = + (BPoint*)MEM_mallocN((cut+1) * sizeof(BPoint), "delNurb7"); + memcpy(nu->bp, nu1->bp,(cut+1)*sizeof(BPoint)); + a= nu1->pntsu-cut-1; + bp = + (BPoint*)MEM_mallocN(a * sizeof(BPoint), "delNurb8"); + memcpy(bp, nu1->bp+cut+1,a*sizeof(BPoint)); + MEM_freeN(nu1->bp); + nu1->bp= bp; + nu1->pntsu= a; + nu->pntsu= cut+1; + } + } + } + } + else if(type==2) + freeNurblist(editnurb); + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +static int delete_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + Object *obedit= CTX_data_edit_object(C); + uiPopupMenu *pup; + uiLayout *layout; + + if(obedit->type==OB_SURF) { + pup= uiPupMenuBegin(C, "Delete", 0); + layout= uiPupMenuLayout(pup); + uiItemEnumO(layout, NULL, 0, op->type->idname, "type", 0); + uiItemEnumO(layout, NULL, 0, op->type->idname, "type", 2); + uiPupMenuEnd(C, pup); + } + else { + pup= uiPupMenuBegin(C, "Delete", 0); + layout= uiPupMenuLayout(pup); + uiItemsEnumO(layout, op->type->idname, "type"); + uiPupMenuEnd(C, pup); + } + + return OPERATOR_CANCELLED; +} + +void CURVE_OT_delete(wmOperatorType *ot) +{ + static EnumPropertyItem type_items[] = { + {0, "SELECTED", "Selected", ""}, + {1, "SEGMENT", "Segment", ""}, + {2, "ALL", "All", ""}, + {0, NULL, NULL, NULL}}; + + /* identifiers */ + ot->name= "Delete"; + ot->idname= "CURVE_OT_delete"; + + /* api callbacks */ + ot->exec= delete_exec; + ot->invoke= delete_invoke; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, "type", type_items, 0, "Type", "Which elements to delete."); +} + +/********************** set smooth operator *********************/ + +static int set_smooth_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + int clear= RNA_boolean_get(op->ptr, "clear"); + + if(obedit->type != OB_CURVE) + return OPERATOR_CANCELLED; + + for(nu= editnurb->first; nu; nu= nu->next) { + if(isNurbsel(nu)) { + if(!clear) nu->flag |= CU_SMOOTH; + else nu->flag &= ~CU_SMOOTH; + } + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_smooth_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Set Smooth"; + ot->idname= "CURVE_OT_smooth_set"; + + /* api callbacks */ + ot->exec= set_smooth_exec; + ot->poll= ED_operator_editsurfcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "clear", 0, "Clear", "Clear smooth shading to solid for selection instead of enabling it."); +} + +/************** join operator, to be used externally? ****************/ + +int join_curve(bContext *C, wmOperator *op, int type) +{ + View3D *v3d= CTX_wm_view3d(C); + Scene *scene= CTX_data_scene(C); + Object *ob= CTX_data_edit_object(C); + Base *base, *nextb; + Curve *cu; + Nurb *nu, *newnu; + BezTriple *bezt; + BPoint *bp; + ListBase tempbase; + float imat[4][4], cmat[4][4]; + int a; + + // XXX not integrated yet, to be called by object/ module? */ + + if(object_data_is_libdata(ob)) { + BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata"); + return OPERATOR_CANCELLED; + } + + if(ob->type!=type) + return 0; + + tempbase.first= tempbase.last= 0; + + /* trasnform all selected curves inverse in obact */ + Mat4Invert(imat, ob->obmat); + + for(base= FIRSTBASE; base; base=nextb) { + nextb= base->next; + + if(TESTBASE(v3d, base)) { + if(base->object->type==type) { + if(base->object != ob) { + + cu= base->object->data; + + if(cu->nurb.first) { + /* watch it: switch order here really goes wrong */ + Mat4MulMat4(cmat, base->object->obmat, imat); + + nu= cu->nurb.first; + while(nu) { + newnu= duplicateNurb(nu); + BLI_addtail(&tempbase, newnu); + + if( (bezt= newnu->bezt) ) { + a= newnu->pntsu; + while(a--) { + Mat4MulVecfl(cmat, bezt->vec[0]); + Mat4MulVecfl(cmat, bezt->vec[1]); + Mat4MulVecfl(cmat, bezt->vec[2]); + bezt++; + } + } + if( (bp= newnu->bp) ) { + a= newnu->pntsu*nu->pntsv; + while(a--) { + Mat4MulVecfl(cmat, bp->vec); + bp++; + } + } + nu= nu->next; + } + } + + ED_base_object_free_and_unlink(scene, base); + } + } + } + } + + cu= ob->data; + addlisttolist(&cu->nurb, &tempbase); + + DAG_scene_sort(scene); // because we removed object(s), call before editmode! + + ED_object_enter_editmode(C, EM_WAITCURSOR); + ED_object_exit_editmode(C, EM_FREEDATA|EM_WAITCURSOR); + + // BIF_undo_push("Join"); + + return OPERATOR_FINISHED; +} + +/************ add primitive, used by object/ module ****************/ + +Nurb *add_nurbs_primitive(bContext *C, int type, int newname) +{ + static int xzproj= 0; /* this function calls itself... */ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + View3D *v3d= CTX_wm_view3d(C); + RegionView3D *rv3d= CTX_wm_region_view3d(C); + Nurb *nu = NULL; + BezTriple *bezt; + BPoint *bp; + float *curs, cent[3],vec[3],imat[3][3],mat[3][3]; + float fac,cmat[3][3], grid; + int a, b, cutype, stype; + + cutype= type & CU_TYPE; // poly, bezier, nurbs, etc + stype= type & CU_PRIMITIVE; + + if (v3d) grid = v3d->grid; + else grid = 1.0; + + /* imat and center and size */ + if(obedit) { + + Mat3CpyMat4(mat, obedit->obmat); + if(v3d) { + curs= give_cursor(scene, v3d); + VECCOPY(cent, curs); + } + else + cent[0]= cent[1]= cent[2]= 0.0f; + + cent[0]-= obedit->obmat[3][0]; + cent[1]-= obedit->obmat[3][1]; + cent[2]-= obedit->obmat[3][2]; + + if(rv3d) { + if (!(newname) || U.flag & USER_ADD_VIEWALIGNED || !rv3d) + Mat3CpyMat4(imat, rv3d->viewmat); + else + Mat3One(imat); + + Mat3MulVecfl(imat, cent); + Mat3MulMat3(cmat, imat, mat); + Mat3Inv(imat, cmat); + } + setflagsNurb(editnurb, 0); + } + else { + return NULL; + } + + /* these types call this function to return a Nurb */ + if (stype!=CU_PRIM_TUBE && stype!=CU_PRIM_DONUT) { + nu = (Nurb*)MEM_callocN(sizeof(Nurb), "addNurbprim"); + nu->type= type; + nu->resolu= 4; + nu->resolv= 4; + } + + switch(stype) { + case CU_PRIM_CURVE: /* curve */ + nu->resolu= 12; /* set as 4 above */ + if(newname) { + rename_id((ID *)obedit, "Curve"); + rename_id((ID *)obedit->data, "Curve"); + } + if(cutype==CU_BEZIER) { + nu->pntsu= 2; + nu->bezt = + (BezTriple*)MEM_callocN(2 * sizeof(BezTriple), "addNurbprim1"); + bezt= nu->bezt; + bezt->h1= bezt->h2= HD_ALIGN; + bezt->f1= bezt->f2= bezt->f3= SELECT; + bezt->radius = 1.0; + + for(a=0;a<3;a++) { + VECCOPY(bezt->vec[a], cent); + } + bezt->vec[1][0]+= -grid; + bezt->vec[0][0]+= -1.5*grid; + bezt->vec[0][1]+= -0.5*grid; + bezt->vec[2][0]+= -0.5*grid; + bezt->vec[2][1]+= 0.5*grid; + for(a=0;a<3;a++) Mat3MulVecfl(imat, bezt->vec[a]); + + bezt++; + bezt->h1= bezt->h2= HD_ALIGN; + bezt->f1= bezt->f2= bezt->f3= SELECT; + bezt->radius = bezt->weight = 1.0; + + for(a=0;a<3;a++) { + VECCOPY(bezt->vec[a], cent); + } + bezt->vec[1][0]+= grid; + for(a=0;a<3;a++) Mat3MulVecfl(imat, bezt->vec[a]); + + calchandlesNurb(nu); + } + else { + + nu->pntsu= 4; + nu->pntsv= 1; + nu->orderu= 4; + nu->bp= callocstructN(BPoint, 4, "addNurbprim3"); + + bp= nu->bp; + for(a=0;a<4;a++, bp++) { + VECCOPY(bp->vec, cent); + bp->vec[3]= 1.0; + bp->f1= SELECT; + bp->radius = bp->weight = 1.0; + } + + bp= nu->bp; + bp->vec[0]+= -1.5*grid; + bp++; + bp->vec[0]+= -grid; + bp->vec[1]+= grid; + bp++; + bp->vec[0]+= grid; + bp->vec[1]+= grid; + bp++; + bp->vec[0]+= 1.5*grid; + + bp= nu->bp; + for(a=0;a<4;a++, bp++) Mat3MulVecfl(imat,bp->vec); + + if(cutype==CU_NURBS) { + nu->knotsu= 0; /* makeknots allocates */ + makeknots(nu, 1); + } + + } + break; + case CU_PRIM_PATH: /* 5 point path */ + nu->pntsu= 5; + nu->pntsv= 1; + nu->orderu= 5; + nu->flagu= 2; /* endpoint */ + nu->resolu= 8; + nu->bp= callocstructN(BPoint, 5, "addNurbprim3"); + + bp= nu->bp; + for(a=0;a<5;a++, bp++) { + VECCOPY(bp->vec, cent); + bp->vec[3]= 1.0; + bp->f1= SELECT; + bp->radius = bp->weight = 1.0; + } + + bp= nu->bp; + bp->vec[0]+= -2.0*grid; + bp++; + bp->vec[0]+= -grid; + bp++; bp++; + bp->vec[0]+= grid; + bp++; + bp->vec[0]+= 2.0*grid; + + bp= nu->bp; + for(a=0;a<5;a++, bp++) Mat3MulVecfl(imat,bp->vec); + + if(cutype==CU_NURBS) { + nu->knotsu= 0; /* makeknots allocates */ + makeknots(nu, 1); + } + + break; + case CU_PRIM_CIRCLE: /* circle */ + nu->resolu= 12; /* set as 4 above */ + if(newname) { + rename_id((ID *)obedit, "CurveCircle"); + rename_id((ID *)obedit->data, "CurveCircle"); + } + if(cutype==CU_BEZIER) { + nu->pntsu= 4; + nu->bezt= callocstructN(BezTriple, 4, "addNurbprim1"); + nu->flagu= CU_CYCLIC; + bezt= nu->bezt; + + for(a=0;a<3;a++) { + VECCOPY(bezt->vec[a], cent); + } + bezt->h1= bezt->h2= HD_AUTO; + bezt->f1= bezt->f2= bezt->f3= SELECT; + bezt->vec[1][0]+= -grid; + for(a=0;a<3;a++) Mat3MulVecfl(imat,bezt->vec[a]); + bezt->radius = bezt->weight = 1.0; + + bezt++; + for(a=0;a<3;a++) { + VECCOPY(bezt->vec[a], cent); + } + bezt->h1= bezt->h2= HD_AUTO; + bezt->f1= bezt->f2= bezt->f3= SELECT; + bezt->vec[1][1]+= grid; + for(a=0;a<3;a++) Mat3MulVecfl(imat,bezt->vec[a]); + bezt->radius = bezt->weight = 1.0; + + bezt++; + for(a=0;a<3;a++) { + VECCOPY(bezt->vec[a], cent); + } + bezt->h1= bezt->h2= HD_AUTO; + bezt->f1= bezt->f2= bezt->f3= SELECT; + bezt->vec[1][0]+= grid; + for(a=0;a<3;a++) Mat3MulVecfl(imat,bezt->vec[a]); + bezt->radius = bezt->weight = 1.0; + + bezt++; + for(a=0;a<3;a++) { + VECCOPY(bezt->vec[a], cent); + } + bezt->h1= bezt->h2= HD_AUTO; + bezt->f1= bezt->f2= bezt->f3= SELECT; + bezt->vec[1][1]+= -grid; + for(a=0;a<3;a++) Mat3MulVecfl(imat,bezt->vec[a]); + bezt->radius = bezt->weight = 1.0; + + calchandlesNurb(nu); + } + else if( cutype==CU_NURBS ) { /* nurb */ + nu->pntsu= 8; + nu->pntsv= 1; + nu->orderu= 4; + nu->bp= callocstructN(BPoint, 8, "addNurbprim6"); + nu->flagu= CU_CYCLIC; + bp= nu->bp; + + for(a=0; a<8; a++) { + bp->f1= SELECT; + VECCOPY(bp->vec, cent); + + if(xzproj==0) { + bp->vec[0]+= nurbcircle[a][0]*grid; + bp->vec[1]+= nurbcircle[a][1]*grid; + } + else { + bp->vec[0]+= 0.25*nurbcircle[a][0]*grid-.75*grid; + bp->vec[2]+= 0.25*nurbcircle[a][1]*grid; + } + if(a & 1) bp->vec[3]= 0.25*sqrt(2.0); + else bp->vec[3]= 1.0; + Mat3MulVecfl(imat,bp->vec); + bp->radius = bp->weight = 1.0; + + bp++; + } + + makeknots(nu, 1); + } + break; + case CU_PRIM_PATCH: /* 4x4 patch */ + if( cutype==CU_NURBS ) { /* nurb */ + if(newname) { + rename_id((ID *)obedit, "Surf"); + rename_id((ID *)obedit->data, "Surf"); + } + + nu->pntsu= 4; + nu->pntsv= 4; + nu->orderu= 4; + nu->orderv= 4; + nu->flag= CU_SMOOTH; + nu->bp= callocstructN(BPoint, 4*4, "addNurbprim6"); + nu->flagu= 0; + nu->flagv= 0; + bp= nu->bp; + + for(a=0; a<4; a++) { + for(b=0; b<4; b++) { + VECCOPY(bp->vec, cent); + bp->f1= SELECT; + fac= (float)a -1.5; + bp->vec[0]+= fac*grid; + fac= (float)b -1.5; + bp->vec[1]+= fac*grid; + if(a==1 || a==2) if(b==1 || b==2) { + bp->vec[2]+= grid; + } + Mat3MulVecfl(imat,bp->vec); + bp->vec[3]= 1.0; + bp++; + } + } + + makeknots(nu, 1); + makeknots(nu, 2); + } + break; + case CU_PRIM_TUBE: /* tube */ + if( cutype==CU_NURBS ) { + if(newname) { + rename_id((ID *)obedit, "SurfTube"); + rename_id((ID *)obedit->data, "SurfTube"); + } + + nu= add_nurbs_primitive(C, CU_NURBS|CU_PRIM_CIRCLE, 0); /* circle */ + nu->resolu= 4; + nu->flag= CU_SMOOTH; + BLI_addtail(editnurb, nu); /* temporal for extrude and translate */ + vec[0]=vec[1]= 0.0; + vec[2]= -grid; + Mat3MulVecfl(imat, vec); + translateflagNurb(editnurb, 1, vec); + extrudeflagNurb(editnurb, 1); + vec[0]= -2*vec[0]; + vec[1]= -2*vec[1]; + vec[2]= -2*vec[2]; + translateflagNurb(editnurb, 1, vec); + + BLI_remlink(editnurb, nu); + + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a-- >0) { + bp->f1 |= SELECT; + bp++; + } + } + break; + case CU_PRIM_SPHERE: /* sphere */ + if( cutype==CU_NURBS ) { + if(newname) { + rename_id((ID *)obedit, "SurfSphere"); + rename_id((ID *)obedit->data, "SurfSphere"); + } + + nu->pntsu= 5; + nu->pntsv= 1; + nu->orderu= 3; + nu->resolu= 4; + nu->resolv= 4; + nu->flag= CU_SMOOTH; + nu->bp= callocstructN(BPoint, 5, "addNurbprim6"); + nu->flagu= 0; + bp= nu->bp; + + for(a=0; a<5; a++) { + bp->f1= SELECT; + VECCOPY(bp->vec, cent); + bp->vec[0]+= nurbcircle[a][0]*grid; + bp->vec[2]+= nurbcircle[a][1]*grid; + if(a & 1) bp->vec[3]= 0.5*sqrt(2.0); + else bp->vec[3]= 1.0; + Mat3MulVecfl(imat,bp->vec); + bp++; + } + nu->flagu= 4; + makeknots(nu, 1); + + BLI_addtail(editnurb, nu); /* temporal for spin */ + if(newname && (U.flag & USER_ADD_VIEWALIGNED) == 0) + spin_nurb(C, scene, obedit, 0, 2); + else + spin_nurb(C, scene, obedit, 0, 0); + + makeknots(nu, 2); + + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a-- >0) { + bp->f1 |= SELECT; + bp++; + } + BLI_remlink(editnurb, nu); + } + break; + case CU_PRIM_DONUT: /* donut */ + if( cutype==CU_NURBS ) { + if(newname) { + rename_id((ID *)obedit, "SurfDonut"); + rename_id((ID *)obedit->data, "SurfDonut"); + } + + xzproj= 1; + nu= add_nurbs_primitive(C, CU_NURBS|CU_PRIM_CIRCLE, 0); /* circle */ + xzproj= 0; + nu->resolu= 4; + nu->resolv= 4; + nu->flag= CU_SMOOTH; + BLI_addtail(editnurb, nu); /* temporal for extrude and translate */ + if(newname && (U.flag & USER_ADD_VIEWALIGNED) == 0) + spin_nurb(C, scene, obedit, 0, 2); + else + spin_nurb(C, scene, obedit, 0, 0); + + BLI_remlink(editnurb, nu); + + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a-- >0) { + bp->f1 |= SELECT; + bp++; + } + + } + break; + } + + /* always do: */ + nu->flag= CU_SMOOTH; + + test2DNurb(nu); + + return nu; +} + +/***************** clear tilt operator ********************/ + +static int clear_tilt_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + ListBase *editnurb= curve_get_editcurve(obedit); + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + int a; + + for(nu= editnurb->first; nu; nu= nu->next) { + if( nu->bezt ) { + bezt= nu->bezt; + a= nu->pntsu; + while(a--) { + if(BEZSELECTED_HIDDENHANDLES(bezt)) bezt->alfa= 0.0; + bezt++; + } + } + else if(nu->bp) { + bp= nu->bp; + a= nu->pntsu*nu->pntsv; + while(a--) { + if(bp->f1 & SELECT) bp->alfa= 0.0; + bp++; + } + } + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +void CURVE_OT_tilt_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Clear Tilt"; + ot->idname= "CURVE_OT_tilt_clear"; + + /* api callbacks */ + ot->exec= clear_tilt_exec; + ot->poll= ED_operator_editcurve; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/****************** undo for curves ****************/ + +static void undoCurve_to_editCurve(void *lbu, void *lbe) +{ + ListBase *lb= lbu; + ListBase *editnurb= lbe; + Nurb *nu, *newnu; + + freeNurblist(editnurb); + + /* copy */ + for(nu= lb->first; nu; nu= nu->next) { + newnu= duplicateNurb(nu); + BLI_addtail(editnurb, newnu); + } +} + +static void *editCurve_to_undoCurve(void *lbe) +{ + ListBase *editnurb= lbe; + ListBase *lb; + Nurb *nu, *newnu; + + lb= MEM_callocN(sizeof(ListBase), "listbase undo"); + + /* copy */ + for(nu= editnurb->first; nu; nu= nu->next) { + newnu= duplicateNurb(nu); + BLI_addtail(lb, newnu); + } + return lb; +} + +static void free_undoCurve(void *lbv) +{ + ListBase *lb= lbv; + + freeNurblist(lb); + MEM_freeN(lb); +} + +static void *get_data(bContext *C) +{ + Object *obedit= CTX_data_edit_object(C); + return curve_get_editcurve(obedit); +} + +/* and this is all the undo system needs to know */ +void undo_push_curve(bContext *C, char *name) +{ + undo_editmode_push(C, name, get_data, free_undoCurve, undoCurve_to_editCurve, editCurve_to_undoCurve, NULL); +} + +/***************** XXX old cruft ********************/ + +void default_curve_ipo(Scene *scene, Curve *cu) +{ +#if 0 // XXX old animation system + IpoCurve *icu; + BezTriple *bezt; + + if(cu->ipo) return; + + cu->ipo= add_ipo(scene, "CurveIpo", ID_CU); + + icu= MEM_callocN(sizeof(IpoCurve), "ipocurve"); + + icu->blocktype= ID_CU; + icu->adrcode= CU_SPEED; + icu->flag= IPO_VISIBLE|IPO_SELECT|IPO_AUTO_HORIZ; + set_icu_vars(icu); + + BLI_addtail( &(cu->ipo->curve), icu); + + icu->bezt= bezt= MEM_callocN(2*sizeof(BezTriple), "defaultipo"); + icu->totvert= 2; + + bezt->hide= IPO_BEZ; + bezt->f1=bezt->f2= bezt->f3= SELECT; + bezt->h1= bezt->h2= HD_AUTO; + bezt++; + bezt->vec[1][0]= 100.0; + bezt->vec[1][1]= 1.0; + bezt->hide= IPO_BEZ; + bezt->f1=bezt->f2= bezt->f3= SELECT; + bezt->h1= bezt->h2= HD_AUTO; + + calchandles_ipocurve(icu); +#endif // XXX old animation system +} + diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c new file mode 100644 index 00000000000..1b2c8ea6b11 --- /dev/null +++ b/source/blender/editors/curve/editfont.c @@ -0,0 +1,1625 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <wchar.h> + +#ifndef WIN32 +#include <unistd.h> +#else +#include <io.h> +#endif + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +#include "DNA_curve_types.h" +#include "DNA_object_types.h" +#include "DNA_vfont_types.h" +#include "DNA_scene_types.h" +#include "DNA_text_types.h" +#include "DNA_view3d_types.h" +#include "DNA_userdef_types.h" + +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_font.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_report.h" +#include "BKE_utildefines.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_curve.h" +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_util.h" + +#include "curve_intern.h" + +#define MAXTEXT 32766 + +/************************* utilities ******************************/ + +static char findaccent(char char1, unsigned int code) +{ + char new= 0; + + if(char1=='a') { + if(code=='`') new= 224; + else if(code==39) new= 225; + else if(code=='^') new= 226; + else if(code=='~') new= 227; + else if(code=='"') new= 228; + else if(code=='o') new= 229; + else if(code=='e') new= 230; + else if(code=='-') new= 170; + } + else if(char1=='c') { + if(code==',') new= 231; + if(code=='|') new= 162; + } + else if(char1=='e') { + if(code=='`') new= 232; + else if(code==39) new= 233; + else if(code=='^') new= 234; + else if(code=='"') new= 235; + } + else if(char1=='i') { + if(code=='`') new= 236; + else if(code==39) new= 237; + else if(code=='^') new= 238; + else if(code=='"') new= 239; + } + else if(char1=='n') { + if(code=='~') new= 241; + } + else if(char1=='o') { + if(code=='`') new= 242; + else if(code==39) new= 243; + else if(code=='^') new= 244; + else if(code=='~') new= 245; + else if(code=='"') new= 246; + else if(code=='/') new= 248; + else if(code=='-') new= 186; + else if(code=='e') new= 143; + } + else if(char1=='s') { + if(code=='s') new= 167; + } + else if(char1=='u') { + if(code=='`') new= 249; + else if(code==39) new= 250; + else if(code=='^') new= 251; + else if(code=='"') new= 252; + } + else if(char1=='y') { + if(code==39) new= 253; + else if(code=='"') new= 255; + } + else if(char1=='A') { + if(code=='`') new= 192; + else if(code==39) new= 193; + else if(code=='^') new= 194; + else if(code=='~') new= 195; + else if(code=='"') new= 196; + else if(code=='o') new= 197; + else if(code=='e') new= 198; + } + else if(char1=='C') { + if(code==',') new= 199; + } + else if(char1=='E') { + if(code=='`') new= 200; + else if(code==39) new= 201; + else if(code=='^') new= 202; + else if(code=='"') new= 203; + } + else if(char1=='I') { + if(code=='`') new= 204; + else if(code==39) new= 205; + else if(code=='^') new= 206; + else if(code=='"') new= 207; + } + else if(char1=='N') { + if(code=='~') new= 209; + } + else if(char1=='O') { + if(code=='`') new= 210; + else if(code==39) new= 211; + else if(code=='^') new= 212; + else if(code=='~') new= 213; + else if(code=='"') new= 214; + else if(code=='/') new= 216; + else if(code=='e') new= 141; + } + else if(char1=='U') { + if(code=='`') new= 217; + else if(code==39) new= 218; + else if(code=='^') new= 219; + else if(code=='"') new= 220; + } + else if(char1=='Y') { + if(code==39) new= 221; + } + else if(char1=='1') { + if(code=='4') new= 188; + if(code=='2') new= 189; + } + else if(char1=='3') { + if(code=='4') new= 190; + } + else if(char1==':') { + if(code=='-') new= 247; + } + else if(char1=='-') { + if(code==':') new= 247; + if(code=='|') new= 135; + if(code=='+') new= 177; + } + else if(char1=='|') { + if(code=='-') new= 135; + if(code=='=') new= 136; + } + else if(char1=='=') { + if(code=='|') new= 136; + } + else if(char1=='+') { + if(code=='-') new= 177; + } + + if(new) return new; + else return char1; +} + + +void update_string(Curve *cu) +{ + EditFont *ef= cu->editfont; + int len; + + // Free the old curve string + MEM_freeN(cu->str); + + // Calculate the actual string length in UTF-8 variable characters + len = wcsleninu8(ef->textbuf); + + // Alloc memory for UTF-8 variable char length string + cu->str = MEM_callocN(len + sizeof(wchar_t), "str"); + + // Copy the wchar to UTF-8 + wcs2utf8s(cu->str, ef->textbuf); +} + +static int insert_into_textbuf(Object *obedit, uintptr_t c) +{ + Curve *cu= obedit->data; + + if(cu->len<MAXTEXT-1) { + EditFont *ef= cu->editfont; + int x; + + for(x= cu->len; x>cu->pos; x--) ef->textbuf[x]= ef->textbuf[x-1]; + for(x= cu->len; x>cu->pos; x--) ef->textbufinfo[x]= ef->textbufinfo[x-1]; + ef->textbuf[cu->pos]= c; + ef->textbufinfo[cu->pos] = cu->curinfo; + ef->textbufinfo[cu->pos].kern = 0; + if(obedit->actcol>0) + ef->textbufinfo[cu->pos].mat_nr = obedit->actcol; + else + ef->textbufinfo[cu->pos].mat_nr = 0; + + cu->pos++; + cu->len++; + ef->textbuf[cu->len]='\0'; + + update_string(cu); + + return 1; + } + else + return 0; +} + +static void text_update_edited(bContext *C, Scene *scene, Object *obedit, int recalc, int mode) +{ + Curve *cu= obedit->data; + EditFont *ef= cu->editfont; + + if(cu->pos) + cu->curinfo = ef->textbufinfo[cu->pos-1]; + else + cu->curinfo = ef->textbufinfo[0]; + + if(obedit->totcol>0) + obedit->actcol= ef->textbufinfo[cu->pos-1].mat_nr; + + update_string(cu); + BKE_text_to_curve(scene, obedit, mode); + + if(recalc) + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); +} + +/********************** insert lorem operator *********************/ + +static int insert_lorem_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + char *p, *p2; + int i; + static char *lastlorem; + + if(lastlorem) + p= lastlorem; + else + p= ED_lorem; + + i= rand()/(RAND_MAX/6)+4; + + for(p2=p; *p2 && i; p2++) { + insert_into_textbuf(obedit, *p2); + + if(*p2=='.') + i--; + } + + lastlorem = p2+1; + if(strlen(lastlorem)<5) + lastlorem = ED_lorem; + + insert_into_textbuf(obedit, '\n'); + insert_into_textbuf(obedit, '\n'); + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + + return OPERATOR_FINISHED; +} + +void FONT_OT_insert_lorem(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Insert Lorem"; + ot->idname= "FONT_OT_insert_lorem"; + + /* api callbacks */ + ot->exec= insert_lorem_exec; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/******************* paste file operator ********************/ + +/* note this handles both ascii and utf8 unicode, previously + * there were 3 functions that did effectively the same thing. */ + +static int paste_file(bContext *C, ReportList *reports, char *filename) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + Curve *cu= obedit->data; + EditFont *ef= cu->editfont; + FILE *fp; + int filelen; + char *strp; + + fp= fopen(filename, "r"); + + if(!fp) { + if(reports) + BKE_reportf(reports, RPT_ERROR, "Failed to open file %s.", filename); + return OPERATOR_CANCELLED; + } + + fseek(fp, 0L, SEEK_END); + filelen = ftell(fp); + fseek(fp, 0L, SEEK_SET); + + strp= MEM_callocN(filelen+4, "tempstr"); + + // fread() instead of read(), because windows read() converts text + // to DOS \r\n linebreaks, causing double linebreaks in the 3d text + filelen = fread(strp, 1, filelen, fp); + fclose(fp); + strp[filelen]= 0; + + if(cu->len+filelen<MAXTEXT) { + int tmplen; + wchar_t *mem = MEM_callocN((sizeof(wchar_t)*filelen)+(4*sizeof(wchar_t)), "temporary"); + tmplen = utf8towchar(mem, strp); + wcscat(ef->textbuf, mem); + MEM_freeN(mem); + cu->len += tmplen; + cu->pos= cu->len; + } + MEM_freeN(strp); + + text_update_edited(C, scene, obedit, 1, 0); + + return OPERATOR_FINISHED; +} + +static int paste_file_exec(bContext *C, wmOperator *op) +{ + char *filename; + int retval; + + filename= RNA_string_get_alloc(op->ptr, "filename", NULL, 0); + retval= paste_file(C, op->reports, filename); + MEM_freeN(filename); + + return retval; +} + +static int paste_file_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + if(RNA_property_is_set(op->ptr, "filename")) + return paste_file_exec(C, op); + + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +void FONT_OT_file_paste(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Paste File"; + ot->idname= "FONT_OT_file_paste"; + + /* api callbacks */ + ot->exec= paste_file_exec; + ot->invoke= paste_file_invoke; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_string_file_path(ot->srna, "filename", "", 0, "Filename", "File path of text file to load."); +} + +/******************* paste buffer operator ********************/ + +static int paste_buffer_exec(bContext *C, wmOperator *op) +{ + char *filename; + +#ifdef WIN32 + filename= "C:\\windows\\temp\\cutbuf.txt"; + +// The following is more likely to work on all Win32 installations. +// suggested by Douglas Toltzman. Needs windows include files... +/* + char tempFileName[MAX_PATH]; + DWORD pathlen; + static const char cutbufname[]="cutbuf.txt"; + + if((pathlen=GetTempPath(sizeof(tempFileName),tempFileName)) > 0 && + pathlen + sizeof(cutbufname) <= sizeof(tempFileName)) + { + strcat(tempFileName,cutbufname); + filename= tempFilename; + } +*/ +#else + filename= "/tmp/.cutbuffer"; +#endif + + return paste_file(C, NULL, filename); +} + +void FONT_OT_buffer_paste(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Paste Buffer"; + ot->idname= "FONT_OT_buffer_paste"; + + /* api callbacks */ + ot->exec= paste_buffer_exec; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/******************* text to object operator ********************/ + +static void txt_add_object(bContext *C, TextLine *firstline, int totline, float offset[3]) +{ + Scene *scene= CTX_data_scene(C); + Curve *cu; + Object *obedit; + Base *base; + struct TextLine *tmp; + int nchars = 0, a; + + obedit= add_object(scene, OB_FONT); + base= scene->basact; + + ED_object_base_init_from_view(C, base); + where_is_object(scene, obedit); + + obedit->loc[0] += offset[0]; + obedit->loc[1] += offset[1]; + obedit->loc[2] += offset[2]; + + cu= obedit->data; + cu->vfont= get_builtin_font(); + cu->vfont->id.us++; + + for(tmp=firstline, a=0; cu->len<MAXTEXT && a<totline; tmp=tmp->next, a++) + nchars += strlen(tmp->line) + 1; + + if(cu->str) MEM_freeN(cu->str); + if(cu->strinfo) MEM_freeN(cu->strinfo); + + cu->str= MEM_callocN(nchars+4, "str"); + cu->strinfo= MEM_callocN((nchars+4)*sizeof(CharInfo), "strinfo"); + + cu->str[0]= '\0'; + cu->len= 0; + cu->pos= 0; + + for(tmp=firstline, a=0; cu->len<MAXTEXT && a<totline; tmp=tmp->next, a++) { + strcat(cu->str, tmp->line); + cu->len+= strlen(tmp->line); + + if(tmp->next) { + strcat(cu->str, "\n"); + cu->len++; + } + + cu->pos= cu->len; + } + + WM_event_add_notifier(C, NC_OBJECT|NA_ADDED, obedit); +} + +void ED_text_to_object(bContext *C, Text *text, int split_lines) +{ + RegionView3D *rv3d= CTX_wm_region_view3d(C); + TextLine *line; + float offset[3]; + int linenum= 0; + + if(!text || !text->lines.first) return; + + if(split_lines) { + for(line=text->lines.first; line; line=line->next) { + /* skip lines with no text, but still make space for them */ + if(line->line[0] == '\0') { + linenum++; + continue; + } + + /* do the translation */ + offset[0] = 0; + offset[1] = -linenum; + offset[2] = 0; + + if(rv3d) + Mat4Mul3Vecfl(rv3d->viewinv, offset); + + txt_add_object(C, line, 1, offset); + + linenum++; + } + } + else { + offset[0]= 0.0f; + offset[1]= 0.0f; + offset[2]= 0.0f; + + txt_add_object(C, text->lines.first, BLI_countlist(&text->lines), offset); + } +} + +/********************** utilities ***************************/ + +static short next_word(Curve *cu) +{ + short s; + for(s=cu->pos; (cu->str[s]) && (cu->str[s]!=' ') && (cu->str[s]!='\n') && + (cu->str[s]!=1) && (cu->str[s]!='\r'); s++); + if(cu->str[s]) return(s+1); else return(s); +} + +static short prev_word(Curve *cu) +{ + short s; + + if(cu->pos==0) return(0); + for(s=cu->pos-2; (cu->str[s]) && (cu->str[s]!=' ') && (cu->str[s]!='\n') && + (cu->str[s]!=1) && (cu->str[s]!='\r'); s--); + if(cu->str[s]) return(s+1); else return(s); +} + +static int kill_selection(Object *obedit, int ins) /* 1 == new character */ +{ + Curve *cu= obedit->data; + EditFont *ef= cu->editfont; + int selend, selstart, direction; + int offset = 0; + int getfrom; + + direction = BKE_font_getselection(obedit, &selstart, &selend); + if(direction) { + int size; + if(ins) offset = 1; + if(cu->pos >= selstart) cu->pos = selstart+offset; + if((direction == -1) && ins) { + selstart++; + selend++; + } + getfrom = selend+offset; + if(ins==0) getfrom++; + size = (cu->len * sizeof(wchar_t)) - (selstart * sizeof(wchar_t)) + (offset*sizeof(wchar_t)); + memmove(ef->textbuf+selstart, ef->textbuf+getfrom, size); + memmove(ef->textbufinfo+selstart, ef->textbufinfo+getfrom, ((cu->len-selstart)+offset)*sizeof(CharInfo)); + cu->len -= (selend-selstart)+offset; + cu->selstart = cu->selend = 0; + } + + return(direction); +} + +/******************* set style operator ********************/ + +static EnumPropertyItem style_items[]= { + {CU_BOLD, "BOLD", "Bold", ""}, + {CU_ITALIC, "ITALIC", "Italic", ""}, + {CU_UNDERLINE, "UNDERLINE", "Underline", ""}, + {0, NULL, NULL, NULL}}; + +static int set_style(bContext *C, int style, int clear) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + Curve *cu= obedit->data; + EditFont *ef= cu->editfont; + int i, selstart, selend; + + if(!BKE_font_getselection(obedit, &selstart, &selend)) + return OPERATOR_CANCELLED; + + for(i=selstart; i<=selend; i++) { + if(clear) + ef->textbufinfo[i].flag &= ~style; + else + ef->textbufinfo[i].flag |= style; + } + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + + return OPERATOR_FINISHED; +} + +static int set_style_exec(bContext *C, wmOperator *op) +{ + int style, clear; + + style= RNA_enum_get(op->ptr, "style"); + clear= RNA_enum_get(op->ptr, "clear"); + + return set_style(C, style, clear); +} + +void FONT_OT_style_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Set Style"; + ot->idname= "FONT_OT_style_set"; + + /* api callbacks */ + ot->exec= set_style_exec; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, "style", style_items, CU_BOLD, "Style", "Style to set selection to."); + RNA_def_boolean(ot->srna, "clear", 0, "Clear", "Clear style rather than setting it."); +} + +/******************* toggle style operator ********************/ + +static int toggle_style_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + Curve *cu= obedit->data; + int style, clear, selstart, selend; + + if(!BKE_font_getselection(obedit, &selstart, &selend)) + return OPERATOR_CANCELLED; + + style= RNA_enum_get(op->ptr, "style"); + + cu->curinfo.flag ^= style; + clear= (cu->curinfo.flag & style) == 0; + + return set_style(C, style, clear); +} + +void FONT_OT_style_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Toggle Style"; + ot->idname= "FONT_OT_style_toggle"; + + /* api callbacks */ + ot->exec= toggle_style_exec; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, "style", style_items, CU_BOLD, "Style", "Style to set selection to."); +} + +/******************* set material operator ********************/ + +static int set_material_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + Curve *cu= obedit->data; + EditFont *ef= cu->editfont; + int i, mat_nr, selstart, selend; + + if(!BKE_font_getselection(obedit, &selstart, &selend)) + return OPERATOR_CANCELLED; + + if(RNA_property_is_set(op->ptr, "index")) + mat_nr= RNA_int_get(op->ptr, "index"); + else + mat_nr= obedit->actcol; + + for(i=selstart; i<=selend; i++) + ef->textbufinfo[i].mat_nr = mat_nr; + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit); + + return OPERATOR_FINISHED; +} + +void FONT_OT_material_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Set Material"; + ot->idname= "FONT_OT_material_set"; + + /* api callbacks */ + ot->exec= set_material_exec; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Material Index", "Material slot index.", 0, INT_MAX); +} + +/******************* copy text operator ********************/ + +static void copy_selection(Object *obedit) +{ + int selstart, selend; + + if(BKE_font_getselection(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)); + } +} + +static int copy_text_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + + copy_selection(obedit); + + return OPERATOR_FINISHED; +} + +void FONT_OT_text_copy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Copy Text"; + ot->idname= "FONT_OT_text_copy"; + + /* api callbacks */ + ot->exec= copy_text_exec; + ot->poll= ED_operator_editfont; +} + +/******************* cut text operator ********************/ + +static int cut_text_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + int selstart, selend; + + if(!BKE_font_getselection(obedit, &selstart, &selend)) + return OPERATOR_CANCELLED; + + copy_selection(obedit); + kill_selection(obedit, 0); + + text_update_edited(C, scene, obedit, 1, 0); + + return OPERATOR_FINISHED; +} + +void FONT_OT_text_cut(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Cut Text"; + ot->idname= "FONT_OT_text_cut"; + + /* api callbacks */ + ot->exec= cut_text_exec; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/******************* paste text operator ********************/ + +static int paste_selection(Object *obedit, ReportList *reports) +{ + Curve *cu= obedit->data; + EditFont *ef= cu->editfont; + int len= wcslen(ef->copybuf); + + // Verify that the copy buffer => [copy buffer len] + cu->len < MAXTEXT + if(cu->len + len <= MAXTEXT) { + if(len) { + int size = (cu->len * sizeof(wchar_t)) - (cu->pos*sizeof(wchar_t)) + sizeof(wchar_t); + memmove(ef->textbuf+cu->pos+len, ef->textbuf+cu->pos, size); + memcpy(ef->textbuf+cu->pos, ef->copybuf, len * sizeof(wchar_t)); + + memmove(ef->textbufinfo+cu->pos+len, ef->textbufinfo+cu->pos, (cu->len-cu->pos+1)*sizeof(CharInfo)); + memcpy(ef->textbufinfo+cu->pos, ef->copybufinfo, len*sizeof(CharInfo)); + + cu->len += len; + cu->pos += len; + + return 1; + } + } + else + BKE_report(reports, RPT_WARNING, "Text too long."); + + return 0; +} + +static int paste_text_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + + if(!paste_selection(obedit, op->reports)) + return OPERATOR_CANCELLED; + + text_update_edited(C, scene, obedit, 1, 0); + + return OPERATOR_FINISHED; +} + +void FONT_OT_text_paste(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Paste Text"; + ot->idname= "FONT_OT_text_paste"; + + /* api callbacks */ + ot->exec= paste_text_exec; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/************************ move operator ************************/ + +static EnumPropertyItem move_type_items[]= { + {LINE_BEGIN, "LINE_BEGIN", "Line Begin", ""}, + {LINE_END, "LINE_END", "Line End", ""}, + {PREV_CHAR, "PREVIOUS_CHARACTER", "Previous Character", ""}, + {NEXT_CHAR, "NEXT_CHARACTER", "Next Character", ""}, + {PREV_WORD, "PREVIOUS_WORD", "Previous Word", ""}, + {NEXT_WORD, "NEXT_WORD", "Next Word", ""}, + {PREV_LINE, "PREVIOUS_LINE", "Previous Line", ""}, + {NEXT_LINE, "NEXT_LINE", "Next Line", ""}, + {PREV_PAGE, "PREVIOUS_PAGE", "Previous Page", ""}, + {NEXT_PAGE, "NEXT_PAGE", "Next Page", ""}, + {0, NULL, NULL, NULL}}; + +static int move_cursor(bContext *C, int type, int select) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + Curve *cu= obedit->data; + EditFont *ef= cu->editfont; + int cursmove= 0; + + switch(type) { + case LINE_BEGIN: + if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1; + while(cu->pos>0) { + if(ef->textbuf[cu->pos-1]=='\n') break; + if(ef->textbufinfo[cu->pos-1].flag & CU_WRAP ) break; + cu->pos--; + } + cursmove=FO_CURS; + break; + + case LINE_END: + if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1; + while(cu->pos<cu->len) { + if(ef->textbuf[cu->pos]==0) break; + if(ef->textbuf[cu->pos]=='\n') break; + if(ef->textbufinfo[cu->pos].flag & CU_WRAP ) break; + cu->pos++; + } + cursmove=FO_CURS; + break; + + case PREV_WORD: + if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1; + cu->pos= prev_word(cu); + cursmove= FO_CURS; + break; + + case NEXT_WORD: + if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1; + cu->pos= next_word(cu); + cursmove= FO_CURS; + break; + + case PREV_CHAR: + if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1; + cu->pos--; + cursmove=FO_CURS; + break; + + case NEXT_CHAR: + if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1; + cu->pos++; + cursmove= FO_CURS; + + break; + + case PREV_LINE: + if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1; + cursmove=FO_CURSUP; + break; + + case NEXT_LINE: + if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1; + cursmove= FO_CURSDOWN; + break; + + case PREV_PAGE: + if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1; + cursmove=FO_PAGEUP; + break; + + case NEXT_PAGE: + if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1; + cursmove=FO_PAGEDOWN; + break; + } + + if(!cursmove) + return OPERATOR_CANCELLED; + + if(select == 0) { + if(cu->selstart) { + cu->selstart = cu->selend = 0; + update_string(cu); + BKE_text_to_curve(scene, obedit, FO_SELCHANGE); + } + } + + if(cu->pos>cu->len) cu->pos= cu->len; + else if(cu->pos>=MAXTEXT) cu->pos= MAXTEXT; + else if(cu->pos<0) cu->pos= 0; + + text_update_edited(C, scene, obedit, select, cursmove); + + if(select) + cu->selend = cu->pos; + + return OPERATOR_FINISHED; +} + +static int move_exec(bContext *C, wmOperator *op) +{ + int type= RNA_enum_get(op->ptr, "type"); + + return move_cursor(C, type, 0); +} + +void FONT_OT_move(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Move Cursor"; + ot->idname= "FONT_OT_move"; + + /* api callbacks */ + ot->exec= move_exec; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to."); +} + +/******************* move select operator ********************/ + +static int move_select_exec(bContext *C, wmOperator *op) +{ + int type= RNA_enum_get(op->ptr, "type"); + + return move_cursor(C, type, 1); +} + +void FONT_OT_move_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Move Select"; + ot->idname= "FONT_OT_move_select"; + + /* api callbacks */ + ot->exec= move_select_exec; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to, to make a selection."); +} + +/************************* change spacing **********************/ + +static int change_spacing_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + Curve *cu= obedit->data; + EditFont *ef= cu->editfont; + int kern, delta= RNA_int_get(op->ptr, "delta"); + + kern = ef->textbufinfo[cu->pos-1].kern; + kern += delta; + CLAMP(kern, -20, 20); + + if(ef->textbufinfo[cu->pos-1].kern == kern) + return OPERATOR_CANCELLED; + + ef->textbufinfo[cu->pos-1].kern = kern; + + text_update_edited(C, scene, obedit, 1, 0); + + return OPERATOR_FINISHED; +} + +void FONT_OT_change_spacing(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Change Spacing"; + ot->idname= "FONT_OT_change_spacing"; + + /* api callbacks */ + ot->exec= change_spacing_exec; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_int(ot->srna, "delta", 1, -20, 20, "Delta", "Amount to decrease or increasing character spacing with.", -20, 20); +} + +/************************* change character **********************/ + +static int change_character_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + Curve *cu= obedit->data; + EditFont *ef= cu->editfont; + int character, delta= RNA_int_get(op->ptr, "delta"); + + if(cu->pos <= 0) + return OPERATOR_CANCELLED; + + character= ef->textbuf[cu->pos - 1]; + character += delta; + CLAMP(character, 0, 255); + + if(character == ef->textbuf[cu->pos - 1]) + return OPERATOR_CANCELLED; + + ef->textbuf[cu->pos - 1]= character; + + text_update_edited(C, scene, obedit, 1, 0); + + return OPERATOR_FINISHED; +} + +void FONT_OT_change_character(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Change Character"; + ot->idname= "FONT_OT_change_character"; + + /* api callbacks */ + ot->exec= change_character_exec; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_int(ot->srna, "delta", 1, -255, 255, "Delta", "Number to increase or decrease character code with.", -255, 255); +} + +/******************* line break operator ********************/ + +static int line_break_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + Curve *cu= obedit->data; + EditFont *ef= cu->editfont; + int ctrl= RNA_enum_get(op->ptr, "ctrl"); + + if(ctrl) { + insert_into_textbuf(obedit, 1); + if(ef->textbuf[cu->pos]!='\n') + insert_into_textbuf(obedit, '\n'); + } + else + insert_into_textbuf(obedit, '\n'); + + cu->selstart = cu->selend = 0; + + text_update_edited(C, scene, obedit, 1, 0); + + return OPERATOR_FINISHED; +} + +void FONT_OT_line_break(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Line Break"; + ot->idname= "FONT_OT_line_break"; + + /* api callbacks */ + ot->exec= line_break_exec; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "ctrl", 0, "Ctrl", ""); // XXX what is this? +} + +/******************* delete operator **********************/ + +static EnumPropertyItem delete_type_items[]= { + {DEL_ALL, "ALL", "All", ""}, + {DEL_NEXT_CHAR, "NEXT_CHARACTER", "Next Character", ""}, + {DEL_PREV_CHAR, "PREVIOUS_CHARACTER", "Previous Character", ""}, + {DEL_SELECTION, "SELECTION", "Selection", ""}, + {DEL_NEXT_SEL, "NEXT_OR_SELECTION", "Next or Selection", ""}, + {DEL_PREV_SEL, "PREVIOUS_OR_SELECTION", "Previous or Selection", ""}, + {0, NULL, NULL, NULL}}; + +static int delete_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + Curve *cu= obedit->data; + EditFont *ef= cu->editfont; + int x, selstart, selend, type= RNA_enum_get(op->ptr, "type"); + + if(cu->len == 0) + return OPERATOR_CANCELLED; + + if(BKE_font_getselection(obedit, &selstart, &selend)) { + if(type == DEL_NEXT_SEL) type= DEL_SELECTION; + else if(type == DEL_PREV_SEL) type= DEL_SELECTION; + } + else { + if(type == DEL_NEXT_SEL) type= DEL_NEXT_CHAR; + else if(type == DEL_PREV_SEL) type= DEL_PREV_CHAR; + } + + switch(type) { + case DEL_ALL: + cu->len = cu->pos = 0; + ef->textbuf[0]= 0; + break; + case DEL_SELECTION: + if(!kill_selection(obedit, 0)) + return OPERATOR_CANCELLED; + break; + case DEL_PREV_CHAR: + if(cu->pos<=0) + return OPERATOR_CANCELLED; + + for(x=cu->pos;x<=cu->len;x++) + ef->textbuf[x-1]= ef->textbuf[x]; + for(x=cu->pos;x<=cu->len;x++) + ef->textbufinfo[x-1]= ef->textbufinfo[x]; + + cu->pos--; + ef->textbuf[--cu->len]='\0'; + break; + case DEL_NEXT_CHAR: + if(cu->pos>=cu->len) + return OPERATOR_CANCELLED; + + for(x=cu->pos;x<cu->len;x++) + ef->textbuf[x]= ef->textbuf[x+1]; + for(x=cu->pos;x<cu->len;x++) + ef->textbufinfo[x]= ef->textbufinfo[x+1]; + + ef->textbuf[--cu->len]='\0'; + break; + default: + return OPERATOR_CANCELLED; + } + + text_update_edited(C, scene, obedit, 1, 0); + + return OPERATOR_FINISHED; +} + +void FONT_OT_delete(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Delete"; + ot->idname= "FONT_OT_delete"; + + /* api callbacks */ + ot->exec= delete_exec; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, "type", delete_type_items, DEL_ALL, "Type", "Which part of the text to delete."); +} + +/*********************** insert text operator *************************/ + +static int insert_text_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + char *inserted_utf8; + wchar_t *inserted_text, first; + int len; + + if(!RNA_property_is_set(op->ptr, "text")) + return OPERATOR_CANCELLED; + + inserted_utf8= RNA_string_get_alloc(op->ptr, "text", NULL, 0); + len= strlen(inserted_utf8); + + inserted_text= MEM_callocN(sizeof(wchar_t)*(len+1), "FONT_insert_text"); + utf8towchar(inserted_text, inserted_utf8); + first= inserted_text[0]; + + MEM_freeN(inserted_text); + MEM_freeN(inserted_utf8); + + if(!first) + return OPERATOR_CANCELLED; + + insert_into_textbuf(obedit, first); + kill_selection(obedit, 1); + text_update_edited(C, scene, obedit, 1, 0); + + return OPERATOR_FINISHED; +} + +static int insert_text_invoke(bContext *C, wmOperator *op, wmEvent *evt) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + Curve *cu= obedit->data; + EditFont *ef= cu->editfont; + static int accentcode= 0; + uintptr_t ascii = evt->ascii; + int alt= evt->alt, shift= evt->shift, ctrl= evt->ctrl; + int event= evt->type, val= evt->val; + wchar_t inserted_text[2]= {0}; + + if(RNA_property_is_set(op->ptr, "text")) + return insert_text_exec(C, op); + + /* tab should exit editmode, but we allow it to be typed using modifier keys */ + if(event==TABKEY) { + if((alt||ctrl||shift) == 0) + return OPERATOR_PASS_THROUGH; + else + ascii= 9; + } + else if(event==BACKSPACEKEY) + ascii= 0; + + if(val && ascii) { + /* handle case like TAB (== 9) */ + if((ascii > 31 && ascii < 254 && ascii != 127) || (ascii==13) || (ascii==10) || (ascii==8)) { + if(accentcode) { + if(cu->pos>0) { + inserted_text[0]= findaccent(ef->textbuf[cu->pos-1], ascii); + ef->textbuf[cu->pos-1]= inserted_text[0]; + } + accentcode= 0; + } + else if(cu->len<MAXTEXT-1) { + if(alt) { + /* might become obsolete, apple has default values for this, other OS's too? */ + if(ascii=='t') ascii= 137; + else if(ascii=='c') ascii= 169; + else if(ascii=='f') ascii= 164; + else if(ascii=='g') ascii= 176; + else if(ascii=='l') ascii= 163; + else if(ascii=='r') ascii= 174; + else if(ascii=='s') ascii= 223; + else if(ascii=='y') ascii= 165; + else if(ascii=='.') ascii= 138; + else if(ascii=='1') ascii= 185; + else if(ascii=='2') ascii= 178; + else if(ascii=='3') ascii= 179; + else if(ascii=='%') ascii= 139; + else if(ascii=='?') ascii= 191; + else if(ascii=='!') ascii= 161; + else if(ascii=='x') ascii= 215; + else if(ascii=='>') ascii= 187; + else if(ascii=='<') ascii= 171; + } + + inserted_text[0]= ascii; + insert_into_textbuf(obedit, ascii); + } + + kill_selection(obedit, 1); + text_update_edited(C, scene, obedit, 1, 0); + } + else { + inserted_text[0]= ascii; + insert_into_textbuf(obedit, ascii); + text_update_edited(C, scene, obedit, 1, 0); + } + } + else if(val && event == BACKSPACEKEY) { + if(alt && cu->len!=0 && cu->pos>0) + accentcode= 1; + + return OPERATOR_PASS_THROUGH; + } + else + return OPERATOR_PASS_THROUGH; + + if(inserted_text[0]) { + /* store as utf8 in RNA string */ + char inserted_utf8[8] = {0}; + + wcs2utf8s(inserted_utf8, inserted_text); + RNA_string_set(op->ptr, "text", inserted_utf8); + } + + return OPERATOR_FINISHED; +} + +void FONT_OT_text_insert(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Insert Text"; + ot->idname= "FONT_OT_text_insert"; + + /* api callbacks */ + ot->exec= insert_text_exec; + ot->invoke= insert_text_invoke; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position."); +} + +/***************** editmode enter/exit ********************/ + +void make_editText(Object *obedit) +{ + Curve *cu= obedit->data; + EditFont *ef= cu->editfont; + + if(ef==NULL) { + ef= cu->editfont= MEM_callocN(sizeof(EditFont), "editfont"); + + 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"); + ef->oldstr= MEM_callocN((MAXTEXT+4)*sizeof(wchar_t), "oldstrbuf"); + ef->oldstrinfo= MEM_callocN((MAXTEXT+4)*sizeof(wchar_t), "oldstrbuf"); + } + + // Convert the original text to wchar_t + utf8towchar(ef->textbuf, cu->str); + wcscpy(ef->oldstr, ef->textbuf); + + cu->len= wcslen(ef->textbuf); + + memcpy(ef->textbufinfo, cu->strinfo, (cu->len)*sizeof(CharInfo)); + memcpy(ef->oldstrinfo, cu->strinfo, (cu->len)*sizeof(CharInfo)); + + if(cu->pos>cu->len) cu->pos= cu->len; + + if(cu->pos) + cu->curinfo = ef->textbufinfo[cu->pos-1]; + else + cu->curinfo = ef->textbufinfo[0]; + + // Convert to UTF-8 + update_string(cu); +} + +void load_editText(Object *obedit) +{ + Curve *cu= obedit->data; + EditFont *ef= cu->editfont; + + MEM_freeN(ef->oldstr); + ef->oldstr= NULL; + MEM_freeN(ef->oldstrinfo); + ef->oldstrinfo= NULL; + + update_string(cu); + + if(cu->strinfo) + MEM_freeN(cu->strinfo); + cu->strinfo= MEM_callocN((cu->len+4)*sizeof(CharInfo), "texteditinfo"); + memcpy(cu->strinfo, ef->textbufinfo, (cu->len)*sizeof(CharInfo)); + + cu->len= strlen(cu->str); + + /* this memory system is weak... */ + + if(cu->selboxes) { + MEM_freeN(cu->selboxes); + cu->selboxes= NULL; + } +} + +void free_editText(Object *obedit) +{ + BKE_free_editfont((Curve *)obedit->data); +} + +/********************** set case operator *********************/ + +static EnumPropertyItem case_items[]= { + {CASE_LOWER, "LOWER", "Lower", ""}, + {CASE_UPPER, "UPPER", "Upper", ""}, + {0, NULL, NULL, NULL}}; + +static int set_case(bContext *C, int ccase) +{ + Scene *scene= CTX_data_scene(C); + Object *obedit= CTX_data_edit_object(C); + Curve *cu= obedit->data; + EditFont *ef= cu->editfont; + wchar_t *str; + int len; + + len= wcslen(ef->textbuf); + str= ef->textbuf; + while(len) { + if(*str>='a' && *str<='z') + *str-= 32; + len--; + str++; + } + + if(ccase == CASE_LOWER) { + len= wcslen(ef->textbuf); + str= ef->textbuf; + while(len) { + if(*str>='A' && *str<='Z') { + *str+= 32; + } + len--; + str++; + } + } + + text_update_edited(C, scene, obedit, 1, 0); + + return OPERATOR_FINISHED; +} + +static int set_case_exec(bContext *C, wmOperator *op) +{ + return set_case(C, RNA_enum_get(op->ptr, "case")); +} + +void FONT_OT_case_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Set Case"; + ot->idname= "FONT_OT_case_set"; + + /* api callbacks */ + ot->exec= set_case_exec; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, "case", case_items, CASE_LOWER, "Case", "Lower or upper case."); +} + +/********************** toggle case operator *********************/ + +static int toggle_case_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + Curve *cu= obedit->data; + EditFont *ef= cu->editfont; + wchar_t *str; + int len, ccase= CASE_UPPER; + + len= wcslen(ef->textbuf); + str= ef->textbuf; + while(len) { + if(*str>='a' && *str<='z') { + ccase= CASE_LOWER; + break; + } + + len--; + str++; + } + + return set_case(C, ccase); +} + +void FONT_OT_case_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Toggle Case"; + ot->idname= "FONT_OT_case_toggle"; + + /* api callbacks */ + ot->exec= toggle_case_exec; + ot->poll= ED_operator_editfont; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* **************** undo for font object ************** */ + +static void undoFont_to_editFont(void *strv, void *ecu) +{ + Curve *cu= (Curve *)ecu; + EditFont *ef= cu->editfont; + char *str= strv; + + cu->pos= *((short *)str); + cu->len= *((short *)(str+2)); + + memcpy(ef->textbuf, str+4, (cu->len+1)*sizeof(wchar_t)); + memcpy(ef->textbufinfo, str+4 + (cu->len+1)*sizeof(wchar_t), cu->len*sizeof(CharInfo)); + + cu->selstart = cu->selend = 0; + + update_string(cu); +} + +static void *editFont_to_undoFont(void *ecu) +{ + Curve *cu= (Curve *)ecu; + EditFont *ef= cu->editfont; + char *str; + + // The undo buffer includes [MAXTEXT+6]=actual string and [MAXTEXT+4]*sizeof(CharInfo)=charinfo + str= MEM_callocN((MAXTEXT+6)*sizeof(wchar_t) + (MAXTEXT+4)*sizeof(CharInfo), "string undo"); + + // Copy the string and string information + memcpy(str+4, ef->textbuf, (cu->len+1)*sizeof(wchar_t)); + memcpy(str+4 + (cu->len+1)*sizeof(wchar_t), ef->textbufinfo, cu->len*sizeof(CharInfo)); + + *((short *)str)= cu->pos; + *((short *)(str+2))= cu->len; + + return str; +} + +static void free_undoFont(void *strv) +{ + MEM_freeN(strv); +} + +static void *get_undoFont(bContext *C) +{ + Object *obedit= CTX_data_edit_object(C); + if(obedit && obedit->type==OB_FONT) { + return obedit->data; + } + return NULL; +} + +/* and this is all the undo system needs to know */ +void undo_push_font(bContext *C, char *name) +{ + undo_editmode_push(C, name, get_undoFont, free_undoFont, undoFont_to_editFont, editFont_to_undoFont, NULL); +} + diff --git a/source/blender/editors/curve/lorem.c b/source/blender/editors/curve/lorem.c new file mode 100644 index 00000000000..e552f99d0e8 --- /dev/null +++ b/source/blender/editors/curve/lorem.c @@ -0,0 +1,513 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "curve_intern.h" + +char *ED_lorem = +"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. " +"Aliquam tristique interdum sem. " +"Nullam pretium, tortor non euismod varius, nulla odio sodales nulla, at bibendum lorem metus sed nulla. " +"Vestibulum in lectus at pede blandit viverra. " +"Fusce scelerisque ipsum nec enim. " +"Fusce euismod nunc id enim. " +"In venenatis cursus arcu. " +"Aenean quis dui. " +"Maecenas laoreet. " +"Nulla tempor, arcu pulvinar pretium suscipit, tortor wisi dapibus libero, id ornare felis ipsum suscipit purus. " +"Maecenas ipsum. " +"Morbi cursus. " +"Vestibulum diam purus, commodo et, convallis eu, posuere at, ligula. " +"Nulla aliquam aliquet lorem. " +"Nunc et mauris hendrerit est bibendum suscipit. " +"Donec pellentesque libero eu nisl. " +"Pellentesque eget libero. " +"Donec tempus ipsum sed quam. " +"Sed blandit nunc quis enim. " +"Quisque lectus diam, adipiscing hendrerit, placerat non, pulvinar id, felis. " +"In congue magna sit amet urna. " +"Nunc non augue sed nisl dictum laoreet. " +"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. " +"In venenatis dapibus massa. " +"Nulla hendrerit sapien et quam. " +"Nunc ac magna lobortis tellus tincidunt posuere. " +"Cras augue mauris, mattis lobortis, fermentum at, semper ac, tellus. " +"Cras vitae ligula sit amet sem posuere iaculis. " +"Aliquam condimentum eleifend felis. " +"Ut sit amet sapien. " +"Suspendisse potenti. " +"Mauris urna. " +"Ut eu enim eu ante porta vestibulum. " +"Aenean scelerisque est ac felis. " +"Suspendisse auctor. " +"Nunc pellentesque. " +"Morbi laoreet ante et nibh. " +"Donec feugiat arcu eget enim. " +"Morbi vehicula tortor ac ipsum. " +"Quisque lacus arcu, elementum ac, faucibus vel, posuere id, est. " +"Proin commodo gravida sem. " +"Vivamus tincidunt vehicula libero. " +"Phasellus wisi. " +"Maecenas pretium tellus eu sapien. " +"Nunc sit amet nunc. " +"In hac habitasse platea dictumst. " +"Aenean dictum neque sed tortor. " +"Donec et erat. " +"Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed justo turpis, scelerisque ut, mattis sit amet, ornare rutrum, massa. " +"Vestibulum bibendum enim sit amet velit. " +"Vivamus tellus ipsum, luctus ut, consectetuer vitae, dignissim non, ligula. " +"Phasellus lacinia wisi at est. " +"Donec elit wisi, commodo non, placerat in, convallis id, elit. " +"Nunc dolor dolor, vestibulum id, bibendum vitae, lacinia id, erat. " +"Cras sit amet eros. " +"Suspendisse suscipit lobortis lectus. " +"Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. " +"Cras orci. " +"Praesent massa urna, lobortis semper, auctor a, pretium vitae, tellus. " +"Curabitur at purus. " +"Morbi tortor quam, imperdiet venenatis, egestas a, cursus eu, est. " +"Nunc interdum lectus sit amet libero. " +"Quisque dignissim placerat ligula. " +"Nunc porttitor posuere arcu. " +"Mauris faucibus quam at massa. " +"Vivamus sodales aliquet mauris. " +"In id ante. " +"Pellentesque varius ipsum in arcu. " +"Fusce mauris lacus, tristique ac, lobortis quis, lobortis luctus, pede. " +"Sed wisi. " +"Vestibulum mattis. " +"Maecenas hendrerit sem nec purus. " +"Proin id quam. " +"Cras nec mauris. " +"Integer orci. " +"Nullam dui sem, molestie sed, egestas quis, cursus in, magna. " +"Mauris neque lacus, consectetuer nec, sagittis eu, porttitor eget, dui. " +"Fusce consectetuer. " +"Donec nec tellus quis leo lobortis ullamcorper. " +"Etiam metus urna, aliquet pretium, ultrices eu, cursus ut, turpis. " +"Morbi bibendum vehicula lectus. " +"Sed non ante vitae arcu pellentesque tempor. " +"Fusce sed ligula in sem tempor imperdiet. " +"Aliquam vel est. " +"Phasellus sollicitudin sollicitudin nibh. " +"Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. " +"Sed vel leo nec eros blandit imperdiet. " +"Nulla facilisi. " +"Suspendisse lobortis, dui ut fringilla hendrerit, justo purus ullamcorper ligula, ultricies ultrices dolor enim in libero. " +"Sed elementum, pede eget porta convallis, dui nulla dignissim pede, eget vehicula odio ante at sem. " +"Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. " +"Sed ullamcorper tincidunt ipsum. " +"Fusce risus nibh, accumsan sit amet, tempus eget, tristique ac, neque. " +"Sed quis lorem ut tortor facilisis fermentum. " +"Fusce pulvinar quam sit amet ipsum. " +"Morbi ac elit quis tellus malesuada blandit. " +"Maecenas suscipit sollicitudin sem. " +"Nam sed eros vel lacus lobortis congue. " +"Proin interdum nunc lobortis orci. " +"Donec egestas enim eu odio. " +"Vestibulum id metus. " +"Pellentesque auctor, sem varius luctus tempus, libero magna cursus neque, et porttitor diam diam quis purus. " +"Vestibulum sit amet dolor. " +"Nulla in magna. " +"Cras id diam at lectus faucibus placerat. " +"Nunc porta posuere sapien. " +"Etiam scelerisque. " +"Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. " +"Aliquam pellentesque mi sed tortor. " +"Maecenas eleifend diam non urna. " +"Donec luctus pharetra tellus. " +"Proin ac ipsum eget libero bibendum volutpat. " +"Cras id tellus. " +"Nulla non risus. " +"In dolor. " +"Fusce scelerisque quam in massa ultrices porta. " +"Morbi ut dolor eu massa egestas condimentum. " +"Mauris vulputate. " +"In hac habitasse platea dictumst. " +"Suspendisse potenti. " +"Mauris vehicula leo in tortor. " +"Mauris vel erat a urna laoreet semper. " +"Pellentesque ut metus ac tellus commodo eleifend. " +"Suspendisse quis urna. " +"Curabitur lacinia dignissim dui. " +"Nam nec ante. " +"In id enim. " +"Aenean mattis enim. " +"In ut neque porttitor risus hendrerit tincidunt. " +"Suspendisse potenti. " +"Ut vestibulum lectus vitae tortor. " +"Duis velit. " +"Nulla facilisi. " +"Integer sit amet urna. " +"Cras varius tortor in pede. " +"Sed facilisis. " +"Praesent lacinia libero nec nibh. " +"Donec aliquam risus non nisl. " +"Nam a nunc et felis tempor feugiat. " +"Nunc metus. " +"Vestibulum euismod, metus in semper laoreet, urna ipsum pharetra lorem, sed ultricies magna lorem sit amet wisi. " +"Sed wisi. " +"Nullam facilisis elit sed nisl. " +"Phasellus mattis leo nec massa. " +"Aenean malesuada. " +"Cras wisi erat, lobortis nec, cursus eget, lobortis at, libero. " +"In massa nisl, rutrum non, cursus nec, faucibus sed, lacus. " +"Pellentesque malesuada. " +"Cras euismod, neque ac suscipit tempus, velit lorem luctus elit, dapibus rhoncus wisi est ut sem. " +"Proin vulputate enim in eros elementum accumsan. " +"Ut lorem nisl, hendrerit et, interdum nec, lacinia in, dolor. " +"Duis nec quam. " +"Praesent velit felis, posuere id, luctus quis, laoreet ut, ipsum. " +"Praesent eget arcu. " +"Mauris massa felis, ornare non, ultrices id, tristique in, elit. " +"Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. " +"Phasellus vulputate, mi ac bibendum facilisis, libero enim suscipit leo, non nonummy pede diam eget nibh. " +"Sed tempus. " +"Aenean interdum suscipit dui. " +"Aliquam erat volutpat. " +"Ut malesuada. " +"Nam commodo, nulla ut fringilla rutrum, orci elit viverra diam, vitae mollis odio odio et purus. " +"Aliquam erat volutpat. " +"Sed aliquet lobortis ipsum. " +"In ut est. " +"Etiam condimentum. " +"Vestibulum pellentesque tortor pulvinar lacus. " +"Aenean orci. " +"Suspendisse lacus nulla, nonummy a, dictum vitae, egestas quis, eros. " +"Donec auctor gravida nisl. " +"Cras ac est rutrum augue pulvinar ornare. " +"Phasellus mauris nibh, vulputate in, rhoncus imperdiet, dapibus eget, lacus. " +"Nam nunc mauris, suscipit at, ultricies a, facilisis at, tellus. " +"Nam accumsan mollis libero. " +"Vivamus condimentum mattis est. " +"Donec lacus. " +"Nullam ac sapien id massa lobortis molestie. " +"Pellentesque elementum. " +"Proin ut purus. " +"Integer et sapien quis turpis commodo mollis. " +"Nulla consequat. " +"Proin a wisi ut tellus blandit elementum. " +"Aliquam nulla lorem, bibendum ac, malesuada vel, elementum et, metus. " +"Phasellus egestas nibh et ligula. " +"Vivamus diam odio, lacinia quis, malesuada quis, sollicitudin ut, eros. " +"Phasellus aliquet lorem ac ipsum. " +"Sed cursus tellus ac orci. " +"Phasellus at nulla. " +"Donec porta sodales ante. " +"Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. " +"Sed id lectus at massa ullamcorper tristique. " +"Suspendisse porttitor lacus. " +"In hac habitasse platea dictumst. " +"Nunc non turpis. " +"Sed sagittis. " +"Morbi laoreet scelerisque dui. " +"Nam arcu tellus, tempor vitae, vestibulum et, imperdiet ut, velit. " +"In sit amet augue a arcu volutpat suscipit. " +"Donec dictum ultrices lectus. " +"Phasellus a purus et orci dictum lacinia. " +"Ut leo. " +"Cras semper, lorem sit amet tincidunt congue, justo eros varius pede, in bibendum turpis lectus non eros. " +"Phasellus quam. " +"Suspendisse mattis sollicitudin magna. " +"Aenean facilisis diam vel nisl nonummy condimentum. " +"Suspendisse vel dolor. " +"Vestibulum nonummy. " +"Mauris imperdiet semper ante. " +"Maecenas vulputate eros. " +"Vestibulum ac dolor. " +"Fusce risus metus, aliquet eget, facilisis et, feugiat vel, orci. " +"Ut at nunc id ante sodales vulputate. " +"Duis tristique mattis ante. " +"Vestibulum neque mauris, laoreet id, congue interdum, aliquet ut, diam. " +"Nulla volutpat blandit magna. " +"Donec accumsan congue diam. " +"Etiam vel dui eget nisl tempor varius. " +"Cras dictum massa sed enim. " +"Cras urna tortor, fringilla ac, ullamcorper in, euismod vel, arcu. " +"Morbi posuere luctus augue. " +"Aliquam dui dui, adipiscing in, lobortis eu, luctus eu, nulla. " +"Cras velit pede, ullamcorper sit amet, feugiat in, auctor vitae, ante. " +"Suspendisse dictum fringilla mauris. " +"In a nibh. " +"Donec ac ligula. " +"In quam. " +"Praesent vitae urna ultricies sem aliquam placerat. " +"Aliquam erat volutpat. " +"Nam est. " +"Donec faucibus sodales metus. " +"Ut congue. " +"Donec arcu tellus, pharetra ac, vulputate ac, mollis sed, lectus. " +"Nulla facilisi. " +"Nullam volutpat nunc et felis. " +"Sed pede odio, tincidunt in, volutpat malesuada, feugiat gravida, nulla. " +"Nulla aliquam pede vitae arcu. " +"Proin velit elit, nonummy sit amet, elementum vitae, varius vitae, dolor. " +"Donec rutrum ipsum eu mi. " +"Aliquam et sem. " +"In adipiscing rhoncus velit. " +"Nam viverra scelerisque arcu. " +"Aliquam sem. " +"Sed tincidunt nulla quis massa. " +"Mauris faucibus tempus nunc. " +"Phasellus condimentum. " +"Curabitur aliquet iaculis sapien. " +"Nunc rhoncus, odio vitae bibendum dignissim, tellus libero commodo ipsum, ut sollicitudin nisl nisl vel justo. " +"Nulla facilisi. " +"Praesent blandit enim ut justo. " +"Proin elementum, elit eget accumsan pulvinar, orci quam auctor neque, sed convallis diam purus vel felis. " +"Sed orci leo, eleifend vel, blandit non, semper eu, purus. " +"Proin bibendum, libero ac consectetuer commodo, eros sapien blandit nisl, eu eleifend nibh nibh vel lectus. " +"Vivamus placerat. " +"Integer odio dolor, pharetra non, sodales id, viverra eget, diam. " +"Nunc mauris magna, egestas quis, feugiat id, fermentum viverra, mi. " +"Aenean suscipit nisl non nunc. " +"Proin quis lectus ac tellus nonummy commodo. " +"Nunc eget diam ac elit vestibulum auctor. " +"Etiam vulputate, odio sed lacinia consequat, justo mi vulputate purus, sit amet euismod libero metus sed tortor. " +"Maecenas ac elit sed lorem vulputate gravida. " +"Proin lectus eros, ullamcorper id, volutpat quis, condimentum tincidunt, sapien. " +"Sed et massa eget lorem aliquet tempus. " +"Duis porttitor nisl non risus. " +"Nam id quam. " +"Nullam est. " +"Proin orci diam, posuere et, pharetra commodo, dictum vel, enim. " +"Proin eget erat. " +"Donec nisl. " +"Maecenas auctor velit ut pede. " +"Nunc vitae lectus nec libero tincidunt hendrerit. " +"Quisque varius, erat ultrices ultrices euismod, purus lacus dictum eros, at condimentum enim dui nec magna. " +"Morbi diam. " +"Phasellus sed est. " +"Phasellus nec libero in arcu fringilla sollicitudin. " +"In rutrum nisl at arcu. " +"Nulla facilisi. " +"Mauris dignissim. " +"Etiam est mauris, pharetra sed, viverra et, tincidunt sed, neque. " +"Ut at lectus id nibh luctus ornare. " +"Mauris varius porttitor risus. " +"Ut vulputate aliquet risus. " +"Vestibulum luctus neque sit amet nunc. " +"Duis fermentum nibh. " +"Pellentesque dapibus. " +"Proin eros libero, aliquam non, condimentum a, sodales ut, turpis. " +"Integer accumsan mi sed lorem. " +"Vestibulum pellentesque sodales nisl. " +"Nulla eu justo quis dui pretium rhoncus. " +"Praesent viverra commodo mi. " +"Maecenas dolor libero, viverra a, elementum vitae, aliquet vitae, dui. " +"Mauris convallis lectus et mi. " +"Mauris sagittis. " +"Sed arcu. " +"Pellentesque auctor. " +"Donec pellentesque purus non tellus. " +"Ut leo wisi, ultrices sit amet, ultrices eu, gravida ac, libero. " +"Mauris fermentum dapibus diam. " +"Integer quis lacus dapibus odio pellentesque varius. " +"Fusce pede quam, vehicula ut, pulvinar et, tincidunt sed, felis. " +"Curabitur eros enim, vulputate sed, aliquam ac, euismod ac, erat. " +"Ut dignissim, lacus a interdum iaculis, enim orci posuere nunc, nec ultricies lectus risus in odio. " +"Etiam et massa id dui commodo vehicula. " +"Nunc blandit tortor quis dui. " +"Quisque nisl. " +"Sed venenatis blandit ligula. " +"Fusce viverra imperdiet magna. " +"Donec eget nunc quis est pharetra lobortis. " +"Vestibulum quis lectus. " +"Mauris vel orci lobortis nunc fermentum bibendum. " +"Pellentesque eget leo. " +"Morbi vel urna sit amet erat fermentum facilisis. " +"Sed vulputate, libero et sollicitudin congue, wisi lectus sodales dolor, eget molestie magna orci vel tellus. " +"Sed tempor ante et enim. " +"Mauris elit. " +"Curabitur ullamcorper vehicula massa. " +"Sed viverra. " +"Duis nulla. " +"Nam bibendum. " +"Nam tortor lorem, ullamcorper vitae, dictum sed, posuere eu, justo. " +"Aliquam adipiscing arcu vitae turpis. " +"Donec malesuada posuere libero. " +"Ut sed tellus. " +"Fusce sed nunc eget nisl dapibus malesuada. " +"Suspendisse potenti. " +"Integer tristique libero et metus. " +"Vivamus posuere. " +"Maecenas non sem non quam fermentum blandit. " +"Duis risus tellus, rutrum vitae, imperdiet nec, malesuada nec, ipsum. " +"Nunc quam dolor, luctus eget, placerat non, rhoncus at, tellus. " +"Duis pede lectus, mattis adipiscing, tempor ut, porta at, mi. " +"Pellentesque risus nulla, sodales sed, interdum id, nonummy vitae, ligula. " +"Morbi pulvinar pede ut massa. " +"Nunc risus mauris, tincidunt et, faucibus eu, suscipit vel, orci. " +"In faucibus felis in arcu. " +"Nulla sit amet elit. " +"Nulla erat sapien, sagittis eget, dignissim eget, viverra eu, felis. " +"Nam ac ipsum. " +"Suspendisse vulputate turpis vel sem lacinia ullamcorper. " +"Mauris ornare ipsum sed ligula. " +"Duis facilisis neque quis orci. " +"Nullam et erat et orci lacinia pellentesque. " +"Donec ac ipsum. " +"Duis molestie ipsum ac arcu. " +"Aenean congue accumsan ante. " +"Integer bibendum, leo ut ornare aliquam, nunc erat condimentum arcu, ut pulvinar mi augue et nulla. " +"Quisque lacinia aliquet wisi. " +"Vivamus nec dui. " +"Etiam wisi leo, euismod vitae, vulputate a, dictum vitae, quam. " +"Quisque quis tortor. " +"Etiam interdum. " +"In massa erat, porttitor sed, tincidunt vel, vehicula fringilla, augue. " +"Nulla vel urna. " +"In libero mi, pretium sed, mattis tempus, sagittis sed, massa. " +"Suspendisse quam wisi, fermentum quis, sagittis at, consequat eget, odio. " +"Nullam imperdiet, purus quis aliquam cursus, turpis odio egestas justo, placerat gravida turpis wisi vel tortor. " +"Nunc ultricies porta purus. " +"Proin elementum erat ac orci. " +"Ut vel magna nec mi feugiat tincidunt. " +"Ut ligula. " +"Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec et magna in diam porta nonummy. " +"Maecenas ut sem in turpis fermentum viverra. " +"Suspendisse at orci. " +"Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. " +"Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Pellentesque rutrum eleifend justo. " +"Nullam vitae pede. " +"Donec condimentum nibh et odio. " +"Sed et metus. " +"Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. " +"Nam tempus. " +"Sed ac wisi. " +"In hac habitasse platea dictumst. " +"Sed sed wisi. " +"Ut facilisis tellus non ligula. " +"Integer metus. " +"In lacinia dui. " +"Curabitur ornare. " +"Mauris vel urna. " +"Nam consectetuer dignissim urna. " +"Nunc elementum porttitor erat. " +"Sed blandit, risus non commodo nonummy, ligula erat fermentum nibh, eu facilisis ante neque sed sem. " +"Etiam scelerisque justo eget wisi. " +"Nunc dignissim. " +"Proin pulvinar quam non lectus. " +"Proin ut turpis quis augue pellentesque dictum. " +"Fusce et lorem. " +"Aliquam urna lacus, blandit sed, vestibulum sit amet, placerat et, dolor. " +"Curabitur auctor erat nec lorem. " +"Phasellus urna wisi, lacinia ut, molestie tincidunt, condimentum id, odio. " +"Curabitur convallis ullamcorper justo. " +"Donec vestibulum est ac quam. " +"Nullam vitae elit eu massa varius vulputate. " +"Nulla facilisi. " +"Suspendisse potenti. " +"Praesent non libero. " +"Nullam tristique massa id magna viverra commodo. " +"Vestibulum libero tortor, luctus ac, viverra congue, consectetuer vel, libero. " +"Aenean arcu augue, luctus id, laoreet pulvinar, dictum sed, lectus. " +"Donec vestibulum volutpat dolor. " +"Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. " +"Pellentesque augue turpis, laoreet nec, malesuada at, nonummy vitae, nibh. " +"Etiam orci sapien, congue in, porttitor sit amet, rutrum vel, nibh. " +"Integer eu lorem. " +"Mauris pretium leo et elit. " +"In nonummy ultricies sapien. " +"Mauris varius. " +"Mauris sed libero. " +"Curabitur ullamcorper elit eu purus. " +"Vestibulum velit pede, semper sit amet, lobortis vitae, tincidunt vel, dui. " +"Nulla neque ante, sagittis eu, vestibulum et, lacinia a, libero. " +"Morbi sit amet wisi. " +"Pellentesque non felis quis arcu bibendum ornare. " +"Aenean enim metus, commodo eu, hendrerit nonummy, euismod ut, quam. " +"Nulla eleifend nisl quis dolor. " +"Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. " +"Maecenas pellentesque massa in erat molestie molestie. " +"Mauris dignissim dapibus libero. " +"Sed sed risus id neque dictum ornare. " +"Sed eu ligula at felis sodales accumsan. " +"Sed interdum, urna non pharetra hendrerit, quam mi ornare libero, id fringilla tortor orci non velit. " +"Aliquam nec risus. " +"Donec at nunc vitae tellus molestie vestibulum. " +"Pellentesque vel justo. " +"Duis ligula libero, vulputate quis, adipiscing bibendum, feugiat vitae, velit. " +"Vivamus et arcu. " +"Fusce eget quam. " +"Ut ante. " +"Suspendisse feugiat metus non ipsum. " +"Nulla tempus leo ut mi. " +"Curabitur vitae nisl. " +"Vivamus elementum. " +"Etiam a orci. " +"Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus urna quam, tincidunt at, ultrices vel, feugiat eget, nulla. " +"Maecenas lacus magna, nonummy eu, iaculis sed, consectetuer quis, enim. " +"Praesent a eros. " +"Aliquam nonummy dignissim neque. " +"Nulla enim. " +"Praesent molestie, orci quis tristique volutpat, lacus metus luctus sapien, et facilisis eros neque id sapien. " +"Nunc condimentum dolor vel orci. " +"Integer wisi diam, porttitor sit amet, feugiat in, dapibus in, lectus. " +"Aliquam erat volutpat. " +"Quisque mollis turpis vitae tortor. " +"Mauris turpis mi, pretium ut, ultrices sed, porta in, justo. " +"Suspendisse posuere. " +"Quisque ultricies lacus vitae enim. " +"Donec lacus. " +"Suspendisse potenti. " +"Donec molestie, magna sed euismod dictum, magna magna interdum diam, vitae sagittis leo lorem ac neque. " +"Cras metus. " +"Quisque nunc. " +"Duis consectetuer. " +"Vestibulum gravida sollicitudin urna. " +"Integer volutpat, massa quis ultrices pulvinar, eros purus dignissim nunc, eget rhoncus enim lectus quis tortor. " +"Integer lacinia quam quis erat convallis mattis. " +"Suspendisse iaculis posuere velit. " +"Etiam tellus enim, aliquet nec, laoreet a, molestie non, velit. " +"Quisque lacus velit, eleifend imperdiet, fringilla id, dapibus scelerisque, lectus. " +"Nulla quis lorem. " +"Nulla malesuada neque et dui. " +"Phasellus malesuada ultricies odio. " +"Phasellus vitae ligula. " +"Pellentesque feugiat arcu at erat. " +"Vivamus ut eros ut lorem pulvinar iaculis. " +"Proin lobortis ipsum id nunc. " +"Curabitur vel massa. " +"Suspendisse nulla ipsum, malesuada vel, posuere eget, mollis at, risus. " +"Vestibulum sed diam id est dapibus ultrices. " +"Proin tempus, eros a scelerisque vestibulum, ipsum arcu aliquam mi, ut feugiat libero odio in nisl. " +"Quisque et massa a mauris luctus congue. " +"Ut id eros. " +"Fusce ante eros, pharetra non, molestie tristique, bibendum sit amet, wisi. " +"Phasellus rutrum, dolor et semper elementum, eros ante malesuada massa, sed sollicitudin lectus velit et massa. " +"In auctor. " +"Aliquam erat volutpat. " +"Etiam risus leo, vulputate suscipit, sollicitudin et, sodales eget, nisl. " +"Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Curabitur lobortis, libero ac laoreet mollis, ligula leo porta wisi, ut euismod felis ligula id elit. " +"Vivamus malesuada nulla eu enim. " +"Donec accumsan faucibus orci. " +"Nulla lacinia ante. " +"Praesent at nibh. " +"Mauris porta dignissim wisi. " +"Ut lacinia tortor nec nunc. " +"Phasellus et augue. " +"Integer rhoncus, libero a pellentesque rhoncus, tortor sapien lobortis pede, eget condimentum sapien risus vitae elit. " +"Suspendisse sed turpis ut dolor placerat dignissim. " +"Quisque quis leo. " +"Cras ultrices. " +"Maecenas hendrerit auctor tortor. " +"Etiam sit amet arcu. "; |