Fix eternal loop in spin tool merge first/last
[blender.git] / source / blender / editors / mesh / editmesh_extrude_spin.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2004 by Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Joseph Eagar
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/mesh/editmesh_extrude_spin.c
29  *  \ingroup edmesh
30  */
31
32 #include "DNA_object_types.h"
33
34 #include "BLI_math.h"
35
36 #include "BKE_context.h"
37 #include "BKE_report.h"
38 #include "BKE_editmesh.h"
39 #include "BKE_layer.h"
40
41 #include "RNA_define.h"
42 #include "RNA_access.h"
43 #include "RNA_enum_types.h"
44
45 #include "WM_api.h"
46 #include "WM_types.h"
47
48 #include "ED_mesh.h"
49 #include "ED_screen.h"
50 #include "ED_view3d.h"
51
52 #include "UI_resources.h"
53
54 #include "MEM_guardedalloc.h"
55
56 #include "mesh_intern.h"  /* own include */
57
58 #define USE_GIZMO
59
60 /* -------------------------------------------------------------------- */
61 /** \name Spin Operator
62  * \{ */
63
64 static int edbm_spin_exec(bContext *C, wmOperator *op)
65 {
66         ViewLayer *view_layer = CTX_data_view_layer(C);
67         float cent[3], axis[3];
68         float d[3] = {0.0f, 0.0f, 0.0f};
69
70         RNA_float_get_array(op->ptr, "center", cent);
71         RNA_float_get_array(op->ptr, "axis", axis);
72         const int steps = RNA_int_get(op->ptr, "steps");
73         const float angle = RNA_float_get(op->ptr, "angle");
74         const bool use_normal_flip = RNA_boolean_get(op->ptr, "use_normal_flip") ^ (angle < 0.0f);
75         const bool dupli = RNA_boolean_get(op->ptr, "dupli");
76         const bool use_auto_merge = (
77                 RNA_boolean_get(op->ptr, "use_auto_merge") &&
78                 (dupli == false) &&
79                 (steps >= 3) &&
80                 fabsf((fabsf(angle) - (float)(M_PI * 2))) <= 1e-6f);
81
82         if (is_zero_v3(axis)) {
83                 BKE_report(op->reports, RPT_ERROR, "Invalid/unset axis");
84                 return OPERATOR_CANCELLED;
85         }
86
87         uint objects_len = 0;
88         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
89
90         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
91                 Object *obedit = objects[ob_index];
92                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
93                 BMesh *bm = em->bm;
94                 BMOperator spinop;
95
96                 /* keep the values in worldspace since we're passing the obmat */
97                 if (!EDBM_op_init(
98                             em, &spinop, op,
99                             "spin geom=%hvef cent=%v axis=%v dvec=%v steps=%i angle=%f space=%m4 "
100                             "use_normal_flip=%b use_duplicate=%b use_merge=%b",
101                             BM_ELEM_SELECT, cent, axis, d, steps, -angle, obedit->obmat,
102                             use_normal_flip, dupli, use_auto_merge))
103                 {
104                         continue;
105                 }
106                 BMO_op_exec(bm, &spinop);
107                 if (use_auto_merge == false) {
108                         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
109                         BMO_slot_buffer_hflag_enable(bm, spinop.slots_out, "geom_last.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
110                 }
111                 if (!EDBM_op_finish(em, &spinop, op, true)) {
112                         continue;
113                 }
114
115                 EDBM_update_generic(em, true, true);
116         }
117
118         MEM_freeN(objects);
119
120         return OPERATOR_FINISHED;
121 }
122
123 /* get center and axis, in global coords */
124 static int edbm_spin_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
125 {
126         Scene *scene = CTX_data_scene(C);
127         View3D *v3d = CTX_wm_view3d(C);
128         RegionView3D *rv3d = ED_view3d_context_rv3d(C);
129
130         PropertyRNA *prop;
131         prop = RNA_struct_find_property(op->ptr, "center");
132         if (!RNA_property_is_set(op->ptr, prop)) {
133                 RNA_property_float_set_array(op->ptr, prop, scene->cursor.location);
134         }
135         if (rv3d) {
136                 prop = RNA_struct_find_property(op->ptr, "axis");
137                 if (!RNA_property_is_set(op->ptr, prop)) {
138                         RNA_property_float_set_array(op->ptr, prop, rv3d->viewinv[2]);
139                 }
140         }
141
142
143 #ifdef USE_GIZMO
144         /* Start with zero angle, drag out the value. */
145         prop = RNA_struct_find_property(op->ptr, "angle");
146         if (!RNA_property_is_set(op->ptr, prop)) {
147                 RNA_property_float_set(op->ptr, prop, 0.0f);
148         }
149 #endif
150
151         int ret = edbm_spin_exec(C, op);
152
153 #ifdef USE_GIZMO
154         if (ret & OPERATOR_FINISHED) {
155                 /* Setup gizmos */
156                 if (v3d && ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0)) {
157                         wmGizmoGroupType *gzgt = WM_gizmogrouptype_find("MESH_GGT_spin_redo", false);
158                         if (!WM_gizmo_group_type_ensure_ptr(gzgt)) {
159                                 struct Main *bmain = CTX_data_main(C);
160                                 WM_gizmo_group_type_reinit_ptr(bmain, gzgt);
161                         }
162                 }
163         }
164 #endif
165
166         return ret;
167 }
168
169 static bool edbm_spin_poll_property(const bContext *UNUSED(C), wmOperator *op, const PropertyRNA *prop)
170 {
171         const char *prop_id = RNA_property_identifier(prop);
172         const bool dupli = RNA_boolean_get(op->ptr, "dupli");
173
174         if (dupli) {
175                 if (STREQ(prop_id, "use_auto_merge") ||
176                     STREQ(prop_id, "use_normal_flip"))
177                 {
178                         return false;
179                 }
180         }
181         return true;
182 }
183
184 void MESH_OT_spin(wmOperatorType *ot)
185 {
186         PropertyRNA *prop;
187
188         /* identifiers */
189         ot->name = "Spin";
190         ot->description = "Extrude selected vertices in a circle around the cursor in indicated viewport";
191         ot->idname = "MESH_OT_spin";
192
193         /* api callbacks */
194         ot->invoke = edbm_spin_invoke;
195         ot->exec = edbm_spin_exec;
196         ot->poll = ED_operator_editmesh;
197         ot->poll_property = edbm_spin_poll_property;
198
199         /* flags */
200         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
201
202         /* props */
203         RNA_def_int(ot->srna, "steps", 9, 0, 1000000, "Steps", "Steps", 0, 1000);
204         RNA_def_boolean(ot->srna, "dupli", 0, "Dupli", "Make Duplicates");
205         prop = RNA_def_float(ot->srna, "angle", DEG2RADF(90.0f), -1e12f, 1e12f, "Angle", "Rotation for each step",
206                              DEG2RADF(-360.0f), DEG2RADF(360.0f));
207         RNA_def_property_subtype(prop, PROP_ANGLE);
208         RNA_def_boolean(ot->srna, "use_auto_merge", true, "Auto Merge", "Merge first/last when the angle is a full revolution");
209         RNA_def_boolean(ot->srna, "use_normal_flip", 0, "Flip Normals", "");
210
211         RNA_def_float_vector(ot->srna, "center", 3, NULL, -1e12f, 1e12f,
212                              "Center", "Center in global view space", -1e4f, 1e4f);
213         RNA_def_float_vector(ot->srna, "axis", 3, NULL, -1.0f, 1.0f, "Axis", "Axis in global view space", -1.0f, 1.0f);
214
215         WM_gizmogrouptype_append(MESH_GGT_spin);
216 #ifdef USE_GIZMO
217         WM_gizmogrouptype_append(MESH_GGT_spin_redo);
218 #endif
219 }
220
221 /** \} */