Update for API change: scene.cursor_location -> scene.cursor.location
[blender-addons.git] / mesh_carver.py
blob1ee3755ee42dfffd0f5a8550778ca4fc063af33f
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
20 bl_info = {
21 "name": "Carver MT",
22 "category": "Object",
23 "author": "Pixivore, Cedric LEPILLER, Ted Milker",
24 "version": (1, 1, 8),
25 "blender": (2, 79, 2),
26 "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
27 "Scripts/Modeling/Carver",
28 "description": "Multiple tools to carve or to create objects",
31 import bpy
32 import bgl
33 import blf
34 import math
35 import sys
36 import random
37 import bmesh
38 from mathutils import (
39 Color,
40 Euler,
41 Matrix,
42 Vector,
43 Quaternion,
45 import bpy_extras
46 from bpy.props import (
47 BoolProperty,
48 IntProperty,
49 PointerProperty,
50 StringProperty,
52 from bpy_extras import view3d_utils
53 from bpy_extras.view3d_utils import (
54 region_2d_to_vector_3d,
55 region_2d_to_location_3d,
59 Profils = [
60 ("CTP_4882",
61 Vector((2.61824, -5.56469, 0)),
62 [(-1.156501, 0.799282, 0.032334),
63 (-0.967583, 0.838861, 0.032334),
64 (-1.10386, 0.846403, 0.032334),
65 (-1.034712, 0.86089, 0.032334),
66 (-1.88472, -0.564419, 0.032334),
67 (-1.924299, -0.375502, 0.032334),
68 (-1.93184, -0.511778, 0.032334),
69 (-1.946327, -0.44263, 0.032334),
70 (-0.219065, -0.869195, 0.032334),
71 (-0.149916, -0.854708, 0.032334),
72 (-0.286193, -0.847167, 0.032334),
73 (-0.097275, -0.807588, 0.032334),
74 (0.692551, 0.434324, 0.032334),
75 (0.678064, 0.503472, 0.032334),
76 (0.670523, 0.367196, 0.032334),
77 (0.630943, 0.556113, 0.032334),
78 (-0.780424, -0.44263, 0.032334),
79 (-0.765937, -0.511778, 0.032334),
80 (-0.758396, -0.375502, 0.032334),
81 (-0.718817, -0.564419, 0.032334),
82 (-0.53496, 0.556113, 0.032334),
83 (-0.49538, 0.367196, 0.032334),
84 (-0.487839, 0.503472, 0.032334),
85 (-0.473352, 0.434324, 0.032334),
86 (-1.263178, -0.807588, 0.032334),
87 (-1.452096, -0.847167, 0.032334),
88 (-1.315819, -0.854708, 0.032334),
89 (-1.384968, -0.869195, 0.032334),
90 (0.131191, 0.86089, 0.032334),
91 (0.062043, 0.846403, 0.032334),
92 (0.19832, 0.838861, 0.032334),
93 (0.009402, 0.799282, 0.032334),
94 (0.946838, -0.869195, 0.032334),
95 (1.015987, -0.854708, 0.032334),
96 (0.87971, -0.847167, 0.032334),
97 (1.068628, -0.807588, 0.032334),
98 (1.858454, 0.434324, 0.032334),
99 (1.843967, 0.503472, 0.032334),
100 (1.836426, 0.367196, 0.032334),
101 (1.796846, 0.556113, 0.032334),
102 (0.385479, -0.44263, 0.032334),
103 (0.399966, -0.511778, 0.032334),
104 (0.407507, -0.375502, 0.032334),
105 (0.447086, -0.564419, 0.032334),
106 (1.297095, 0.86089, 0.032334),
107 (1.227946, 0.846403, 0.032334),
108 (1.364223, 0.838861, 0.032334),
109 (1.175305, 0.799282, 0.032334),
111 [[16, 17, 19], [5, 4, 24], [14, 12, 15], [14, 15, 31], [10, 8, 11], [15, 30, 31], [19, 10, 11],
112 [11, 14, 31], [31, 18, 11], [8, 9, 11], [18, 16, 19], [12, 13, 15], [18, 19, 11], [28, 29, 31],
113 [30, 28, 31], [24, 21, 0], [23, 22, 20], [20, 1, 0], [3, 2, 0], [0, 5, 24], [7, 6, 4], [4, 25, 24],
114 [27, 26, 24], [21, 23, 20], [1, 3, 0], [5, 7, 4], [25, 27, 24], [21, 20, 0], [40, 41, 43], [38, 36, 39],
115 [38, 39, 47], [34, 32, 35], [39, 46, 47], [43, 34, 35], [35, 38, 47], [47, 42, 35], [32, 33, 35],
116 [42, 40, 43], [36, 37, 39], [42, 43, 35], [44, 45, 47], [46, 44, 47]]),
117 ("CTP_8354",
118 Vector((-0.06267, -2.43829, -0.0)),
119 [(-0.534254, -1.0, 0.032334),
120 (-1.0, -0.534254, 0.032334),
121 (-0.654798, -0.98413, 0.032334),
122 (-0.767127, -0.937602, 0.032334),
123 (-0.863586, -0.863586, 0.032334),
124 (-0.937602, -0.767127, 0.032334),
125 (-0.98413, -0.654798, 0.032334),
126 (1.0, -0.534254, 0.032334),
127 (0.534254, -1.0, 0.032334),
128 (0.98413, -0.654798, 0.032334),
129 (0.937602, -0.767127, 0.032334),
130 (0.863586, -0.863586, 0.032334),
131 (0.767127, -0.937602, 0.032334),
132 (0.654798, -0.98413, 0.032334),
133 (-1.0, 0.534254, 0.032334),
134 (-0.534254, 1.0, 0.032334),
135 (-0.98413, 0.654798, 0.032334),
136 (-0.937602, 0.767127, 0.032334),
137 (-0.863586, 0.863586, 0.032334),
138 (-0.767127, 0.937602, 0.032334),
139 (-0.654798, 0.98413, 0.032334),
140 (0.534254, 1.0, 0.032334),
141 (1.0, 0.534254, 0.032334),
142 (0.654798, 0.98413, 0.032334),
143 (0.767127, 0.937602, 0.032334),
144 (0.863586, 0.863586, 0.032334),
145 (0.937602, 0.767127, 0.032334),
146 (0.98413, 0.654798, 0.032334),
147 (-0.763998, 0.518786, 0.032334),
148 (-0.763998, -0.518786, 0.032334),
149 (-0.754202, -0.593189, 0.032334),
150 (-0.731454, -0.648108, 0.032334),
151 (-0.695267, -0.695267, 0.032334),
152 (-0.648108, -0.731454, 0.032334),
153 (-0.593189, -0.754202, 0.032334),
154 (-0.518786, -0.763998, 0.032334),
155 (0.518786, -0.763998, 0.032334),
156 (0.593189, -0.754202, 0.032334),
157 (0.648108, -0.731454, 0.032334),
158 (0.695267, -0.695267, 0.032334),
159 (0.731454, -0.648108, 0.032334),
160 (0.754202, -0.593189, 0.032334),
161 (0.763998, -0.518786, 0.032334),
162 (0.763998, 0.518786, 0.032334),
163 (0.754202, 0.593189, 0.032334),
164 (0.731454, 0.648108, 0.032334),
165 (0.695267, 0.695267, 0.032334),
166 (0.648108, 0.731454, 0.032334),
167 (0.593189, 0.754202, 0.032334),
168 (0.518786, 0.763998, 0.032334),
169 (-0.518786, 0.763998, 0.032334),
170 (-0.593189, 0.754202, 0.032334),
171 (-0.648108, 0.731454, 0.032334),
172 (-0.695267, 0.695267, 0.032334),
173 (-0.731454, 0.648108, 0.032334),
174 (-0.754202, 0.593189, 0.032334),
175 (0.518786, 0.518786, 0.032334),
176 (-0.518786, 0.518786, 0.032334),
177 (0.518786, -0.518786, 0.032334),
178 (-0.518786, -0.518786, 0.032334),
179 (-0.593189, 0.518786, 0.032334),
180 (-0.593189, -0.518786, 0.032334),
181 (0.518786, -0.593189, 0.032334),
182 (-0.518786, -0.593189, 0.032334),
183 (-0.593189, -0.593189, 0.032334),
184 (0.593189, 0.518786, 0.032334),
185 (0.593189, -0.518786, 0.032334),
186 (0.593189, -0.593189, 0.032334),
187 (-0.593189, 0.593189, 0.032334),
188 (-0.518786, 0.593189, 0.032334),
189 (0.518786, 0.593189, 0.032334),
190 (0.593189, 0.593189, 0.032334),
191 (-0.648108, 0.593189, 0.032334),
192 (-0.648108, 0.518786, 0.032334),
193 (-0.648108, -0.518786, 0.032334),
194 (-0.648108, -0.593189, 0.032334),
195 (-0.695267, 0.593189, 0.032334),
196 (-0.695267, 0.518786, 0.032334),
197 (-0.695267, -0.518786, 0.032334),
198 (-0.695267, -0.593189, 0.032334),
199 (0.648108, 0.593189, 0.032334),
200 (0.648108, 0.518786, 0.032334),
201 (0.648108, -0.518786, 0.032334),
202 (0.648108, -0.593189, 0.032334),
203 (0.695267, 0.593189, 0.032334),
204 (0.695267, 0.518786, 0.032334),
205 (0.695267, -0.518786, 0.032334),
206 (0.695267, -0.593189, 0.032334),
208 [[87, 39, 40, 41], [29, 28, 14, 1], [30, 29, 1, 6], [31, 30, 6, 5], [32, 31, 5, 4], [33, 32, 4, 3],
209 [34, 33, 3, 2], [35, 34, 2, 0], [36, 35, 0, 8], [37, 36, 8, 13], [38, 37, 13, 12], [39, 38, 12, 11],
210 [40, 39, 11, 10], [41, 40, 10, 9], [42, 41, 9, 7], [43, 42, 7, 22], [44, 43, 22, 27], [45, 44, 27, 26],
211 [46, 45, 26, 25], [47, 46, 25, 24], [48, 47, 24, 23], [49, 48, 23, 21], [50, 49, 21, 15], [51, 50, 15, 20],
212 [52, 51, 20, 19], [53, 52, 19, 18], [54, 53, 18, 17], [55, 54, 17, 16], [28, 55, 16, 14], [68, 69, 50, 51],
213 [63, 35, 36, 62], [69, 57, 56, 70], [84, 85, 43, 44], [64, 34, 35, 63], [57, 59, 58, 56], [85, 86, 42, 43],
214 [60, 61, 59, 57], [73, 74, 61, 60], [72, 68, 51, 52], [75, 33, 34, 64], [61, 64, 63, 59], [59, 63, 62, 58],
215 [86, 87, 41, 42], [74, 75, 64, 61], [58, 62, 67, 66], [56, 58, 66, 65], [70, 56, 65, 71], [62, 36, 37, 67],
216 [49, 70, 71, 48], [50, 69, 70, 49], [60, 57, 69, 68], [73, 60, 68, 72], [46, 84, 44, 45], [78, 79, 75, 74],
217 [77, 78, 74, 73], [77, 73, 72, 76], [76, 72, 52, 53], [79, 32, 33, 75], [29, 30, 79, 78], [28, 29, 78, 77],
218 [28, 77, 76, 55], [55, 76, 53, 54], [30, 31, 32, 79], [66, 67, 83, 82], [65, 66, 82, 81], [71, 65, 81, 80],
219 [48, 71, 80, 47], [67, 37, 38, 83], [82, 83, 87, 86], [81, 82, 86, 85], [80, 81, 85, 84], [47, 80, 84, 46],
220 [83, 38, 39, 87]]),
221 ("CTP_5585",
222 Vector((5.0114, -2.4281, 0.0)),
223 [(-0.490711, -1.0, 0.032334),
224 (-1.0, -0.490711, 0.032334),
225 (1.0, -0.490711, 0.032334),
226 (0.490711, -1.0, 0.032334),
227 (-1.0, 0.490711, 0.032334),
228 (-0.490711, 1.0, 0.032334),
229 (0.490711, 1.0, 0.032334),
230 (1.0, 0.490711, 0.032334),
231 (-0.51852, 0.291276, 0.032334),
232 (-0.51852, -0.291276, 0.032334),
233 (-0.291276, -0.51852, 0.032334),
234 (0.291276, -0.51852, 0.032334),
235 (0.51852, -0.291276, 0.032334),
236 (0.51852, 0.291276, 0.032334),
237 (0.291276, 0.51852, 0.032334),
238 (-0.291276, 0.51852, 0.032334),
240 [[11, 12, 13, 14], [9, 8, 4, 1], [10, 9, 1, 0], [11, 10, 0, 3], [12, 11, 3, 2], [13, 12, 2, 7],
241 [14, 13, 7, 6], [15, 14, 6, 5], [8, 15, 5, 4], [9, 10, 15, 8], [10, 11, 14, 15]]),
242 ("CTP_6960",
243 Vector((-0.11417, 2.48371, -0.0)),
244 [(0.0, 1.0, 0.016827),
245 (-0.382683, 0.92388, 0.016827),
246 (-0.707107, 0.707107, 0.016827),
247 (-0.92388, 0.382683, 0.016827),
248 (-1.0, -0.0, 0.016827),
249 (-0.92388, -0.382684, 0.016827),
250 (-0.707107, -0.707107, 0.016827),
251 (-0.382683, -0.92388, 0.016827),
252 (-0.0, -1.0, 0.016827),
253 (0.382683, -0.92388, 0.016827),
254 (0.707107, -0.707107, 0.016827),
255 (0.92388, -0.382684, 0.016827),
256 (1.0, 0.0, 0.016827),
257 (0.923879, 0.382684, 0.016827),
258 (0.707107, 0.707107, 0.016827),
259 (0.382683, 0.92388, 0.016827),
260 (-0.0, 0.546859, 0.016827),
261 (-0.209274, 0.505231, 0.016827),
262 (-0.386687, 0.386687, 0.016827),
263 (-0.505231, 0.209274, 0.016827),
264 (-0.546859, -0.0, 0.016827),
265 (-0.505231, -0.209274, 0.016827),
266 (-0.386687, -0.386687, 0.016827),
267 (-0.209274, -0.505231, 0.016827),
268 (-0.0, -0.546859, 0.016827),
269 (0.209274, -0.505231, 0.016827),
270 (0.386687, -0.386688, 0.016827),
271 (0.505231, -0.209274, 0.016827),
272 (0.546858, 0.0, 0.016827),
273 (0.505231, 0.209274, 0.016827),
274 (0.386687, 0.386688, 0.016827),
275 (0.209273, 0.505232, 0.016827),
277 [[3, 19, 18, 2], [11, 27, 26, 10], [4, 20, 19, 3], [12, 28, 27, 11], [5, 21, 20, 4], [13, 29, 28, 12],
278 [6, 22, 21, 5], [14, 30, 29, 13], [7, 23, 22, 6], [15, 31, 30, 14], [8, 24, 23, 7], [1, 17, 16, 0],
279 [0, 16, 31, 15], [9, 25, 24, 8], [2, 18, 17, 1], [10, 26, 25, 9]]),
280 ("CTP_5359",
281 Vector((5.50446, 2.41669, -0.0)),
282 [(0.0, 0.714247, 0.023261),
283 (-0.382683, 0.659879, 0.023261),
284 (-0.707107, 0.505049, 0.023261),
285 (-0.92388, 0.273331, 0.023261),
286 (-1.0, -0.0, 0.023261),
287 (-0.92388, -0.273331, 0.023261),
288 (-0.707107, -0.505049, 0.023261),
289 (-0.382683, -0.659879, 0.023261),
290 (-0.0, -0.714247, 0.023261),
291 (0.382683, -0.659879, 0.023261),
292 (0.707107, -0.505049, 0.023261),
293 (0.92388, -0.273331, 0.023261),
294 (1.0, 0.0, 0.023261),
295 (0.923879, 0.273331, 0.023261),
296 (0.707107, 0.505049, 0.023261),
297 (0.382683, 0.659879, 0.023261),
298 (-0.0, 0.303676, 0.023261),
299 (-0.162705, 0.28056, 0.023261),
300 (-0.30064, 0.214731, 0.023261),
301 (-0.392805, 0.116212, 0.023261),
302 (-0.425169, -0.0, 0.023261),
303 (-0.392805, -0.116212, 0.023261),
304 (-0.30064, -0.214731, 0.023261),
305 (-0.162705, -0.28056, 0.023261),
306 (-0.0, -0.303676, 0.023261),
307 (0.162705, -0.28056, 0.023261),
308 (0.30064, -0.214731, 0.023261),
309 (0.392805, -0.116212, 0.023261),
310 (0.425169, 0.0, 0.023261),
311 (0.392805, 0.116212, 0.023261),
312 (0.30064, 0.214731, 0.023261),
313 (0.162705, 0.28056, 0.023261),
315 [[3, 19, 18, 2], [11, 27, 26, 10], [4, 20, 19, 3], [12, 28, 27, 11], [5, 21, 20, 4], [13, 29, 28, 12],
316 [6, 22, 21, 5], [14, 30, 29, 13], [7, 23, 22, 6], [15, 31, 30, 14], [8, 24, 23, 7], [1, 17, 16, 0],
317 [0, 16, 31, 15], [9, 25, 24, 8], [2, 18, 17, 1], [10, 26, 25, 9]]),
318 ("CTP_5424",
319 Vector((2.61824, 2.34147, 0.0)),
320 [(1.0, -1.0, 0.032334),
321 (-1.0, 1.0, 0.032334),
322 (1.0, 1.0, 0.032334),
323 (0.783867, -0.259989, 0.032334),
324 (-0.393641, 0.857073, 0.032334),
325 (0.73142, -0.116299, 0.032334),
326 (0.657754, 0.02916, 0.032334),
327 (0.564682, 0.172804, 0.032334),
328 (0.454497, 0.311098, 0.032334),
329 (0.329912, 0.440635, 0.032334),
330 (0.193995, 0.558227, 0.032334),
331 (0.050092, 0.660978, 0.032334),
332 (-0.098254, 0.746358, 0.032334),
333 (-0.247389, 0.812263, 0.032334),
335 [[3, 0, 2], [10, 9, 2], [2, 1, 4], [2, 4, 13], [5, 3, 2], [6, 5, 2], [2, 13, 12], [2, 12, 11], [7, 6, 2],
336 [8, 7, 2], [2, 11, 10], [9, 8, 2]]),
337 ("CTP_3774",
338 Vector((2.61824, -2.52425, 0.0)),
339 [(1.0, 0.0, 0.020045),
340 (-1.0, 0.0, 0.020045),
341 (0.31903, -0.664947, 0.020045),
342 (-0.31903, -0.664947, 0.020045),
343 (-0.31903, 1.0, 0.020045),
344 (0.31903, 1.0, 0.020045),
345 (0.31903, 0.0, 0.020045),
346 (-0.31903, 0.0, 0.020045),
347 (-1.0, 0.614333, 0.020045),
348 (-0.614333, 1.0, 0.020045),
349 (-0.970643, 0.761921, 0.020045),
350 (-0.887041, 0.887041, 0.020045),
351 (-0.761921, 0.970643, 0.020045),
352 (0.614333, 1.0, 0.020045),
353 (1.0, 0.614333, 0.020045),
354 (0.761921, 0.970643, 0.020045),
355 (0.887041, 0.887041, 0.020045),
356 (0.970643, 0.761921, 0.020045),
357 (-0.31903, 0.614333, 0.020045),
358 (0.31903, 0.614333, 0.020045),
359 (0.31903, 0.761921, 0.020045),
360 (-0.31903, 0.761921, 0.020045),
361 (0.31903, 0.887041, 0.020045),
362 (-0.31903, 0.887041, 0.020045),
363 (0.614333, 0.614333, 0.020045),
364 (0.614333, 0.0, 0.020045),
365 (0.614333, 0.761921, 0.020045),
366 (0.614333, 0.887041, 0.020045),
367 (-0.614333, 0.761921, 0.020045),
368 (-0.614333, 0.0, 0.020045),
369 (-0.614333, 0.887041, 0.020045),
370 (-0.614333, 0.614333, 0.020045),
372 [[6, 25, 24, 19], [6, 19, 18, 7], [2, 6, 7, 3], [1, 29, 31, 8], [8, 31, 28, 10], [19, 24, 26, 20],
373 [18, 19, 20, 21], [21, 20, 22, 23], [10, 28, 30, 11], [20, 26, 27, 22], [22, 27, 13, 5], [23, 22, 5, 4],
374 [11, 30, 9, 12], [17, 16, 27, 26], [14, 17, 26, 24], [24, 25, 0, 14], [15, 13, 27, 16], [9, 30, 23, 4],
375 [31, 29, 7, 18], [28, 31, 18, 21], [30, 28, 21, 23]]),
376 ("CTP_4473",
377 Vector((7.31539, 0.0, 0.0)),
378 [(0.24549, -1.0, 0.022454),
379 (-0.24549, -1.0, 0.022454),
380 (-0.24549, 1.0, 0.022454),
381 (0.24549, 1.0, 0.022454),
382 (1.0, 0.267452, 0.022454),
383 (1.0, -0.267452, 0.022454),
384 (-1.0, -0.267452, 0.022454),
385 (-1.0, 0.267452, 0.022454),
386 (0.24549, 0.267452, 0.022454),
387 (0.24549, -0.267452, 0.022454),
388 (-0.24549, 0.267452, 0.022454),
389 (-0.24549, -0.267452, 0.022454),
391 [[8, 3, 2, 10], [0, 9, 11, 1], [4, 8, 9, 5], [8, 10, 11, 9], [10, 7, 6, 11]]),
392 ("CTP_4003",
393 Vector((4.91276, 0.0, 0.0)),
394 [(-1.0, -1.0, 0.026945),
395 (1.0, -1.0, 0.026945),
396 (-1.0, 1.0, 0.026945),
397 (-0.026763, -1.0, 0.026945),
398 (-0.026763, 1.0, 0.026945),
399 (1.0, -0.026763, 0.026945),
400 (0.238983, 0.965014, 0.026945),
401 (0.486619, 0.86244, 0.026945),
402 (0.699268, 0.699268, 0.026945),
403 (0.86244, 0.486619, 0.026945),
404 (0.965014, 0.238983, 0.026945),
405 (0.238983, -1.0, 0.026945),
406 (0.486619, -1.0, 0.026945),
407 (0.699268, -1.0, 0.026945),
408 (0.86244, -1.0, 0.026945),
409 (-0.026763, 0.479676, 0.026945),
410 (0.486619, 0.479676, 0.026945),
411 (0.699268, 0.479676, 0.026945),
412 (0.238983, 0.479676, 0.026945),
413 (0.865316, 0.479676, 0.026945),
414 (-1.0, 0.479676, 0.026945),
415 (0.86244, 0.479676, 0.026945),
416 (-0.026763, 0.238983, 0.026945),
417 (0.486619, 0.238983, 0.026945),
418 (0.699268, 0.238983, 0.026945),
419 (0.238983, 0.238983, 0.026945),
420 (-1.0, 0.238983, 0.026945),
421 (0.86244, 0.238983, 0.026945),
422 (-0.026763, -0.026763, 0.026945),
423 (0.486619, -0.026763, 0.026945),
424 (0.699268, -0.026763, 0.026945),
425 (0.238983, -0.026763, 0.026945),
426 (-1.0, -0.026763, 0.026945),
427 (0.86244, -0.026763, 0.026945),
429 [[0, 3, 28, 32], [4, 15, 18, 6], [6, 18, 16, 7], [7, 16, 17, 8], [8, 17, 21, 9], [9, 21, 19], [18, 15, 22, 25],
430 [19, 21, 27, 10], [16, 18, 25, 23], [17, 16, 23, 24], [20, 15, 4, 2], [21, 17, 24, 27], [27, 24, 30, 33],
431 [23, 25, 31, 29], [24, 23, 29, 30], [25, 22, 28, 31], [26, 22, 15, 20], [10, 27, 33, 5], [31, 28, 3, 11],
432 [33, 30, 13, 14], [29, 31, 11, 12], [5, 33, 14, 1], [30, 29, 12, 13], [32, 28, 22, 26]]),
433 ("CTP_3430",
434 Vector((2.61824, 0.0, 0.0)),
435 [(-1.0, -1.0, 0.032334),
436 (1.0, -1.0, 0.032334),
437 (-1.0, 1.0, 0.032334),
438 (1.0, 1.0, 0.032334),
440 [[0, 1, 3, 2]]),
441 ("CTP_7175",
442 Vector((0.0, 0.0, 0.0)),
443 [(-1.0, -1.0, 0.032334),
444 (1.0, -1.0, 0.032334),
445 (-1.0, 1.0, 0.032334),
446 (1.0, 1.0, 0.032334),
447 (0.0, 0.0, 0.032334),
448 (0.0, 0.0, 0.032334),
449 (0.0, 0.0, 0.032334),
450 (0.0, 0.0, 0.032334),
451 (0.0, 0.0, 0.032334),
452 (-0.636126, 0.636126, 0.032334),
453 (-0.636126, -0.636126, 0.032334),
454 (0.636126, -0.636126, 0.032334),
455 (0.636126, 0.636126, 0.032334),
457 [[10, 9, 2, 0], [11, 10, 0, 1], [12, 11, 1, 3], [9, 12, 3, 2]]),
460 # Cut Type
461 RECTANGLE = 0
462 LINE = 1
463 CIRCLE = 2
465 # Boolean operation
466 DIFFERENCE = 0
467 UNION = 1
470 class CarverPrefs(bpy.types.AddonPreferences):
471 bl_idname = __name__
473 Enable_Tab_01: BoolProperty(
474 name="Info",
475 description="Some general information and settings about the add-on",
476 default=False
478 Enable_Tab_02: BoolProperty(
479 name="Hotkeys",
480 description="List of the shortcuts used during carving",
481 default=False
483 bpy.types.Scene.Key_Create: StringProperty(
484 name="Object creation",
485 description="Object creation",
486 maxlen=1,
487 default="C"
489 bpy.types.Scene.Key_Update: StringProperty(
490 name="Auto Bevel Update",
491 description="Auto Bevel Update",
492 maxlen=1,
493 default="A",
495 bpy.types.Scene.Key_Bool: StringProperty(
496 name="Boolean type",
497 description="Boolean operation type",
498 maxlen=1,
499 default="T",
501 bpy.types.Scene.Key_Brush: StringProperty(
502 name="Brush Mode",
503 description="Brush Mode",
504 maxlen=1,
505 default="B",
507 bpy.types.Scene.Key_Help: StringProperty(
508 name="Help display",
509 description="Help display",
510 maxlen=1,
511 default="H",
513 bpy.types.Scene.Key_Instant: StringProperty(
514 name="Instantiate",
515 description="Instantiate object",
516 maxlen=1,
517 default="I",
519 bpy.types.Scene.Key_Close: StringProperty(
520 name="Close polygonal shape",
521 description="Close polygonal shape",
522 maxlen=1,
523 default="X",
525 bpy.types.Scene.Key_Apply: StringProperty(
526 name="Apply operation",
527 description="Apply operation",
528 maxlen=1,
529 default="Q",
531 bpy.types.Scene.Key_Scale: StringProperty(
532 name="Scale object",
533 description="Scale object",
534 maxlen=1,
535 default="S",
537 bpy.types.Scene.Key_Gapy: StringProperty(
538 name="Gap rows",
539 description="Scale gap between columns",
540 maxlen=1,
541 default="J",
543 bpy.types.Scene.Key_Gapx: StringProperty(
544 name="Gap columns",
545 description="Scale gap between columns",
546 maxlen=1,
547 default="U",
549 bpy.types.Scene.Key_Depth: StringProperty(
550 name="Depth",
551 description="Cursor depth or solidify pattern",
552 maxlen=1,
553 default="D",
555 bpy.types.Scene.Key_BrushDepth: StringProperty(
556 name="Brush Depth",
557 description="Brush depth",
558 maxlen=1,
559 default="C",
561 bpy.types.Scene.Key_Subadd: StringProperty(
562 name="Add subdivision",
563 description="Add subdivision",
564 maxlen=1,
565 default="X",
567 bpy.types.Scene.Key_Subrem: StringProperty(
568 name="Remove subdivision",
569 description="Remove subdivision",
570 maxlen=1,
571 default="W",
573 bpy.types.Scene.Key_Randrot: StringProperty(
574 name="Random rotation",
575 description="Random rotation",
576 maxlen=1,
577 default="R",
579 bpy.types.Scene.ProfilePrefix: StringProperty(
580 name="Profile prefix",
581 description="Prefix to look for profiles with",
582 default="Carver_Profile-"
585 def draw(self, context):
586 scene = context.scene
587 layout = self.layout
589 icon_1 = "TRIA_RIGHT" if not self.Enable_Tab_01 else "TRIA_DOWN"
590 box = layout.box()
591 box.prop(self, "Enable_Tab_01", text="Info and Settings", emboss=False, icon=icon_1)
592 if self.Enable_Tab_01:
593 box.label(text="Carver Operator:", icon="LAYER_ACTIVE")
594 box.label(text="Select a Mesh Object and press [CTRL]+[SHIFT]+[X] to carve",
595 icon="LAYER_USED")
596 box.label(text="To finish carving press [ESC] or [RIGHT CLICK]",
597 icon="LAYER_USED")
598 box.prop(scene, "ProfilePrefix", text="Profile prefix")
600 icon_2 = "TRIA_RIGHT" if not self.Enable_Tab_02 else "TRIA_DOWN"
601 box = layout.box()
602 box.prop(self, "Enable_Tab_02", text="Keys", emboss=False, icon=icon_2)
603 if self.Enable_Tab_02:
604 split = box.split(align=True)
605 box = split.box()
606 col = box.column(align=True)
607 col.label(text="Object Creation:")
608 col.prop(scene, "Key_Create", text="")
609 col.label(text="Auto bevel update:")
610 col.prop(scene, "Key_Update", text="")
611 col.label(text="Boolean operation type:")
612 col.prop(scene, "Key_Bool", text="")
613 col.label(text="Brush Depth:")
614 col.prop(scene, "Key_BrushDepth", text="")
616 box = split.box()
617 col = box.column(align=True)
618 col.label(text="Brush Mode:")
619 col.prop(scene, "Key_Brush", text="")
620 col.label(text="Help display:")
621 col.prop(scene, "Key_Help", text="")
622 col.label(text="Instantiate object:")
623 col.prop(scene, "Key_Instant", text="")
624 col.label(text="Random rotation:")
625 col.prop(scene, "Key_Randrot", text="")
627 box = split.box()
628 col = box.column(align=True)
629 col.label(text="Close polygonal shape:")
630 col.prop(scene, "Key_Close", text="")
631 col.label(text="Apply operation:")
632 col.prop(scene, "Key_Apply", text="")
633 col.label(text="Scale object:")
634 col.prop(scene, "Key_Scale", text="")
635 col.label(text="Subdiv add:")
636 col.prop(scene, "Key_Subadd", text="")
638 box = split.box()
639 col = box.column(align=True)
640 col.label(text="Gap rows:")
641 col.prop(scene, "Key_Gapy", text="")
642 col.label(text="Gap columns:")
643 col.prop(scene, "Key_Gapx", text="")
644 col.label(text="Depth / Solidify:")
645 col.prop(scene, "Key_Depth", text="")
646 col.label(text="Subdiv Remove:")
647 col.prop(scene, "Key_Subrem", text="")
650 # Draw Text (Center position)
651 def DrawCenterText(text, xt, yt, Size, colors, self):
652 font_id = 0
653 # Offset Shadow
654 Sshadow_x = 2
655 Sshadow_y = -2
657 blf.size(font_id, Size, 72)
658 blf.position(font_id, xt + Sshadow_x - blf.dimensions(font_id, text)[0] / 2, yt + Sshadow_y, 0)
659 bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
661 blf.draw(font_id, text)
662 blf.position(font_id, xt - blf.dimensions(font_id, text)[0] / 2, yt, 0)
663 if colors is not None:
664 mcolor = Color((colors[0], colors[1], colors[2]))
665 bgl.glColor4f(mcolor.r, mcolor.g, mcolor.b, 1.0)
666 else:
667 bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
668 blf.draw(font_id, text)
671 # Draw text (Left position)
672 def DrawLeftText(text, xt, yt, Size, colors, self):
673 font_id = 0
674 # Offset Shadow
675 Sshadow_x = 2
676 Sshadow_y = -2
678 blf.size(font_id, Size, 72)
679 blf.position(font_id, xt + Sshadow_x, yt + Sshadow_y, 0)
680 bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
681 blf.draw(font_id, text)
682 blf.position(font_id, xt, yt, 0)
683 if colors is not None:
684 mcolor = Color((colors[0], colors[1], colors[2]))
685 bgl.glColor4f(mcolor.r, mcolor.g, mcolor.b, 1.0)
686 else:
687 bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
688 blf.draw(font_id, text)
691 # Draw text (Right position)
692 def DrawRightText(text, xt, yt, Size, colors, self):
693 font_id = 0
694 # Offset Shadow
695 Sshadow_x = 2
696 Sshadow_y = -2
698 blf.size(font_id, Size, 72)
699 blf.position(font_id, xt + Sshadow_x - blf.dimensions(font_id, text)[0], yt + Sshadow_y, 0)
700 bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
701 blf.draw(font_id, text)
702 blf.position(font_id, xt - blf.dimensions(font_id, text)[0], yt, 0)
703 if colors is not None:
704 mcolor = Color((colors[0], colors[1], colors[2]))
705 bgl.glColor4f(mcolor.r, mcolor.g, mcolor.b, 1.0)
706 else:
707 bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
708 blf.draw(font_id, text)
711 # Opengl draws
712 def draw_callback_px(self, context):
713 font_id = 0
714 region = context.region
716 # Width screen
717 overlap = context.preferences.system.use_region_overlap
718 t_panel_width = 0
719 if overlap:
720 for region in context.area.regions:
721 if region.type == 'TOOLS':
722 t_panel_width = region.width
724 # Initial position
725 xt = int(region.width / 2.0)
726 yt = 130
727 if region.width >= 850:
728 xt = int(region.width / 2.0)
729 yt = 150
731 # Command Display
732 if self.CreateMode and ((self.ObjectMode is False) and (self.ProfileMode is False)):
733 BooleanMode = "Create"
734 else:
735 if self.ObjectMode or self.ProfileMode:
736 BooleanType = "Difference) [T]" if self.BoolOps == DIFFERENCE else "Union) [T]"
737 BooleanMode = \
738 "Object Brush (" + BooleanType if self.ObjectMode else "Profil Brush (" + BooleanType
739 else:
740 BooleanMode = \
741 "Difference" if (self.shift is False) and (self.ForceRebool is False) else "Rebool"
743 UIColor = (0.992, 0.5518, 0.0, 1.0)
745 # Display boolean mode
746 text_size = 40 if region.width >= 850 else 20
747 DrawCenterText(BooleanMode, xt, yt, text_size, UIColor, self)
749 # Separator (Line)
750 LineWidth = 75
751 if region.width >= 850:
752 LineWidth = 140
753 bgl.glLineWidth(1)
754 bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
755 bgl.glBegin(bgl.GL_LINE_STRIP)
756 bgl.glVertex2i(int(xt - LineWidth), yt - 8)
757 bgl.glVertex2i(int(xt + LineWidth), yt - 8)
758 bgl.glEnd()
760 # Text position
761 xt = xt - blf.dimensions(font_id, "Difference")[0] / 2 + 80
763 # Primitives type
764 PrimitiveType = "Rectangle "
765 if self.CutMode == CIRCLE:
766 PrimitiveType = "Circle "
767 if self.CutMode == LINE:
768 PrimitiveType = "Line "
770 # Variables according to screen size
771 IFontSize = 12
772 yInterval = 20
773 yCmd = yt - 30
775 if region.width >= 850:
776 IFontSize = 18
777 yInterval = 25
779 # Color
780 Color0 = None
781 Color1 = UIColor
783 # Help Display
784 if (self.ObjectMode is False) and (self.ProfileMode is False):
785 TypeStr = "Cut Type [Space] : "
786 if self.CreateMode:
787 TypeStr = "Type [Space] : "
788 blf.size(font_id, IFontSize, 72)
789 OpsStr = TypeStr + PrimitiveType
790 TotalWidth = blf.dimensions(font_id, OpsStr)[0]
791 xLeft = region.width / 2 - TotalWidth / 2
792 xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0]
793 DrawLeftText(TypeStr, xLeft, yCmd, IFontSize, Color0, self)
794 DrawLeftText(PrimitiveType, xLeftP, yCmd, IFontSize, Color1, self)
796 # Depth Cursor
797 TypeStr = "Cursor Depth [" + context.scene.Key_Depth + "] : "
798 BoolStr = "(ON)" if self.snapCursor else "(OFF)"
799 OpsStr = TypeStr + BoolStr
801 TotalWidth = blf.dimensions(font_id, OpsStr)[0]
802 xLeft = region.width / 2 - TotalWidth / 2
803 xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0]
804 DrawLeftText(TypeStr, xLeft, yCmd - yInterval, IFontSize, Color0, self)
805 DrawLeftText(BoolStr, xLeftP, yCmd - yInterval, IFontSize, Color1, self)
807 if self.CreateMode is False:
808 # Apply Booleans
809 TypeStr = "Apply Operations [" + context.scene.Key_Apply + "] : "
810 BoolStr = "(OFF)" if self.DontApply else "(ON)"
811 OpsStr = TypeStr + BoolStr
813 TotalWidth = blf.dimensions(font_id, OpsStr)[0]
814 xLeft = region.width / 2 - TotalWidth / 2
815 xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0]
816 DrawLeftText(TypeStr, xLeft, yCmd - yInterval * 2, IFontSize, Color0, self)
817 DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * 2, IFontSize, Color1, self)
819 # Auto update for bevel
820 TypeStr = "Bevel Update [" + context.scene.Key_Update + "] : "
821 BoolStr = "(ON)" if self.Auto_BevelUpdate else "(OFF)"
822 OpsStr = TypeStr + BoolStr
824 TotalWidth = blf.dimensions(font_id, OpsStr)[0]
825 xLeft = region.width / 2 - TotalWidth / 2
826 xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0]
827 DrawLeftText(TypeStr, xLeft, yCmd - yInterval * 3, IFontSize, Color0, self)
828 DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * 3, IFontSize, Color1, self)
830 # Subdivisions
831 if self.CutMode == CIRCLE:
832 y = yCmd - yInterval * 4 if self.CreateMode is False else yCmd - yInterval * 2
833 TypeStr = "Subdivisions [" + context.scene.Key_Subrem + "][" + context.scene.Key_Subadd + "] : "
834 BoolStr = str((int(360 / self.stepAngle[self.step])))
835 OpsStr = TypeStr + BoolStr
836 TotalWidth = blf.dimensions(font_id, OpsStr)[0]
837 xLeft = region.width / 2 - TotalWidth / 2
838 xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0]
839 DrawLeftText(TypeStr, xLeft, y, IFontSize, Color0, self)
840 DrawLeftText(BoolStr, xLeftP, y, IFontSize, Color1, self)
842 else:
843 # INSTANTIATE:
844 TypeStr = "Instantiate [" + context.scene.Key_Instant + "] : "
845 BoolStr = "(ON)" if self.Instantiate else "(OFF)"
846 OpsStr = TypeStr + BoolStr
848 blf.size(font_id, IFontSize, 72)
849 TotalWidth = blf.dimensions(font_id, OpsStr)[0]
850 xLeft = region.width / 2 - TotalWidth / 2
851 xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0]
852 DrawLeftText(TypeStr, xLeft, yCmd, IFontSize, Color0, self)
853 DrawLeftText(BoolStr, xLeftP, yCmd, IFontSize, Color1, self)
855 # RANDOM ROTATION:
856 if self.alt:
857 TypeStr = "Random Rotation [" + context.scene.Key_Randrot + "] : "
858 BoolStr = "(ON)" if self.RandomRotation else "(OFF)"
859 OpsStr = TypeStr + BoolStr
861 blf.size(font_id, IFontSize, 72)
862 TotalWidth = blf.dimensions(font_id, OpsStr)[0]
863 xLeft = region.width / 2 - TotalWidth / 2
864 xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0]
865 DrawLeftText(TypeStr, xLeft, yCmd - yInterval, IFontSize, Color0, self)
866 DrawLeftText(BoolStr, xLeftP, yCmd - yInterval, IFontSize, Color1, self)
868 # THICKNESS:
869 if self.BrushSolidify:
870 TypeStr = "Thickness [" + context.scene.Key_Depth + "] : "
871 if self.ProfileMode:
872 BoolStr = str(round(self.ProfileBrush.modifiers["CT_SOLIDIFY"].thickness, 2))
873 if self.ObjectMode:
874 BoolStr = str(round(self.ObjectBrush.modifiers["CT_SOLIDIFY"].thickness, 2))
875 OpsStr = TypeStr + BoolStr
876 blf.size(font_id, IFontSize, 72)
877 TotalWidth = blf.dimensions(font_id, OpsStr)[0]
878 xLeft = region.width / 2 - TotalWidth / 2
879 xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0]
881 self_alt_y = 2 if self.alt else 1
882 DrawLeftText(TypeStr, xLeft, yCmd - yInterval * self_alt_y, IFontSize, Color0, self)
883 DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * self_alt_y, IFontSize, Color1, self)
885 # BRUSH DEPTH:
886 if (self.ObjectMode):
887 TypeStr = "Carve Depth [" + context.scene.Key_Depth + "] : "
888 BoolStr = str(round(self.ObjectBrush.data.vertices[0].co.z, 2))
889 OpsStr = TypeStr + BoolStr
891 blf.size(font_id, IFontSize, 72)
892 TotalWidth = blf.dimensions(font_id, OpsStr)[0]
893 xLeft = region.width / 2 - TotalWidth / 2
894 xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0]
896 self_alt_y = 2 if self.alt else 1
897 DrawLeftText(TypeStr, xLeft, yCmd - yInterval * self_alt_y, IFontSize, Color0, self)
898 DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * self_alt_y, IFontSize, Color1, self)
900 TypeStr = "Brush Depth [" + context.scene.Key_BrushDepth + "] : "
901 BoolStr = str(round(self.BrushDepthOffset, 2))
902 OpsStr = TypeStr + BoolStr
904 blf.size(font_id, IFontSize, 72)
905 TotalWidth = blf.dimensions(font_id, OpsStr)[0]
906 xLeft = region.width / 2 - TotalWidth / 2
907 xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0]
909 self_alt_y = 3 if self.alt else 2
910 DrawLeftText(TypeStr, xLeft, yCmd - yInterval * self_alt_y, IFontSize, Color0, self)
911 DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * self_alt_y, IFontSize, Color1, self)
913 bgl.glEnable(bgl.GL_BLEND)
914 if region.width >= 850:
915 if self.AskHelp is False:
916 xrect = 40
917 yrect = 40
918 bgl.glColor4f(0.0, 0.0, 0.0, 0.3)
919 bgl.glRecti(xrect, yrect, xrect + 90, yrect + 25)
920 DrawLeftText("[" + context.scene.Key_Help + "] for help", xrect + 10, yrect + 8, 13, None, self)
921 else:
922 xHelp = 30 + t_panel_width
923 yHelp = 80
924 Help_FontSize = 12
925 Help_Interval = 14
926 if region.width >= 850:
927 Help_FontSize = 15
928 Help_Interval = 20
929 yHelp = 220
931 if self.ObjectMode or self.ProfileMode:
932 if self.ProfileMode:
933 DrawLeftText("[" + context.scene.Key_Brush + "]", xHelp, yHelp +
934 Help_Interval * 2, Help_FontSize, UIColor, self)
935 DrawLeftText(": Object Mode", 150 + t_panel_width, yHelp +
936 Help_Interval * 2, Help_FontSize, None, self)
937 else:
938 DrawLeftText("[" + context.scene.Key_Brush + "]", xHelp, yHelp +
939 Help_Interval * 2, Help_FontSize, UIColor, self)
940 DrawLeftText(": Return", 150 + t_panel_width, yHelp +
941 Help_Interval * 2, Help_FontSize, None, self)
942 else:
943 DrawLeftText("[" + context.scene.Key_Brush + "]", xHelp, yHelp +
944 Help_Interval * 2, Help_FontSize, UIColor, self)
945 DrawLeftText(": Profil Brush", 150 + t_panel_width, yHelp +
946 Help_Interval * 2, Help_FontSize, None, self)
947 DrawLeftText("[Ctrl + LMB]", xHelp, yHelp - Help_Interval * 6,
948 Help_FontSize, UIColor, self)
949 DrawLeftText(": Move Cursor", 150 + t_panel_width, yHelp -
950 Help_Interval * 6, Help_FontSize, None, self)
952 if (self.ObjectMode is False) and (self.ProfileMode is False):
953 if self.CreateMode is False:
954 DrawLeftText("[" + context.scene.Key_Create + "]", xHelp,
955 yHelp + Help_Interval, Help_FontSize, UIColor, self)
956 DrawLeftText(": Create geometry", 150 + t_panel_width,
957 yHelp + Help_Interval, Help_FontSize, None, self)
958 else:
959 DrawLeftText("[" + context.scene.Key_Create + "]", xHelp,
960 yHelp + Help_Interval, Help_FontSize, UIColor, self)
961 DrawLeftText(": Cut", 150 + t_panel_width, yHelp + Help_Interval,
962 Help_FontSize, None, self)
964 if self.CutMode == RECTANGLE:
965 DrawLeftText("MouseMove", xHelp, yHelp, Help_FontSize, UIColor, self)
966 DrawLeftText("[Alt]", xHelp, yHelp - Help_Interval, Help_FontSize, UIColor, self)
967 DrawLeftText(": Dimension", 150 + t_panel_width, yHelp, Help_FontSize, None, self)
968 DrawLeftText(": Move all", 150 + t_panel_width, yHelp - Help_Interval,
969 Help_FontSize, None, self)
971 if self.CutMode == CIRCLE:
972 DrawLeftText("MouseMove", xHelp, yHelp, Help_FontSize, UIColor, self)
973 DrawLeftText("[Alt]", xHelp, yHelp - Help_Interval, Help_FontSize, UIColor, self)
974 DrawLeftText("[" + context.scene.Key_Subrem + "] [" + context.scene.Key_Subadd + "]",
975 xHelp, yHelp - Help_Interval * 2, Help_FontSize, UIColor, self)
976 DrawLeftText("[Ctrl]", xHelp, yHelp - Help_Interval * 3, Help_FontSize, UIColor, self)
977 DrawLeftText(": Rotation and Radius", 150 + t_panel_width, yHelp, Help_FontSize, None, self)
978 DrawLeftText(": Move all", 150 + t_panel_width, yHelp - Help_Interval,
979 Help_FontSize, None, self)
980 DrawLeftText(": Subdivision", 150 + t_panel_width, yHelp -
981 Help_Interval * 2, Help_FontSize, None, self)
982 DrawLeftText(": Incremental rotation", 150 + t_panel_width,
983 yHelp - Help_Interval * 3, Help_FontSize, None, self)
985 if self.CutMode == LINE:
986 DrawLeftText("MouseMove", xHelp, yHelp, Help_FontSize, UIColor, self)
987 DrawLeftText("[Alt]", xHelp, yHelp - Help_Interval, Help_FontSize, UIColor, self)
988 DrawLeftText("[Space]", xHelp, yHelp - Help_Interval * 2, Help_FontSize, UIColor, self)
989 DrawLeftText("[Ctrl]", xHelp, yHelp - Help_Interval * 3, Help_FontSize, UIColor, self)
990 DrawLeftText(": Dimension", 150 + t_panel_width, yHelp, Help_FontSize, None, self)
991 DrawLeftText(": Move all", 150 + t_panel_width, yHelp - Help_Interval,
992 Help_FontSize, None, self)
993 DrawLeftText(": Validate", 150 + t_panel_width, yHelp -
994 Help_Interval * 2, Help_FontSize, None, self)
995 DrawLeftText(": Incremental", 150 + t_panel_width, yHelp -
996 Help_Interval * 3, Help_FontSize, None, self)
997 if self.CreateMode:
998 DrawLeftText("[" + context.scene.Key_Subadd + "]", xHelp, yHelp -
999 Help_Interval * 4, Help_FontSize, UIColor, self)
1000 DrawLeftText(": Close geometry", 150 + t_panel_width, yHelp -
1001 Help_Interval * 4, Help_FontSize, None, self)
1002 else:
1003 DrawLeftText("[Space]", xHelp, yHelp + Help_Interval, Help_FontSize, UIColor, self)
1004 DrawLeftText(": Difference", 150 + t_panel_width, yHelp + Help_Interval,
1005 Help_FontSize, None, self)
1006 DrawLeftText("[Shift][Space]", xHelp, yHelp, Help_FontSize, UIColor, self)
1007 DrawLeftText(": Rebool", 150 + t_panel_width, yHelp, Help_FontSize, None, self)
1008 DrawLeftText("[Alt][Space]", xHelp, yHelp - Help_Interval, Help_FontSize, UIColor, self)
1009 DrawLeftText(": Duplicate", 150 + t_panel_width, yHelp - Help_Interval,
1010 Help_FontSize, None, self)
1011 DrawLeftText("[" + context.scene.Key_Scale + "]", xHelp, yHelp -
1012 Help_Interval * 2, Help_FontSize, UIColor, self)
1013 DrawLeftText(": Scale", 150 + t_panel_width, yHelp - Help_Interval * 2,
1014 Help_FontSize, None, self)
1015 DrawLeftText("[LMB][Move]", xHelp, yHelp - Help_Interval * 3, Help_FontSize, UIColor, self)
1016 DrawLeftText(": Rotation", 150 + t_panel_width, yHelp - Help_Interval * 3,
1017 Help_FontSize, None, self)
1018 DrawLeftText("[Ctrl][LMB][Move]", xHelp, yHelp - Help_Interval * 4,
1019 Help_FontSize, UIColor, self)
1020 DrawLeftText(": Step Angle", 150 + t_panel_width, yHelp - Help_Interval * 4,
1021 Help_FontSize, None, self)
1022 if self.ProfileMode:
1023 DrawLeftText("[" + context.scene.Key_Subadd + "][" + context.scene.Key_Subrem + "]",
1024 xHelp, yHelp - Help_Interval * 5, Help_FontSize, UIColor, self)
1025 DrawLeftText(": Previous or Next Profile", 150 + t_panel_width,
1026 yHelp - Help_Interval * 5, Help_FontSize, None, self)
1027 DrawLeftText("[ARROWS]", xHelp, yHelp - Help_Interval * 6, Help_FontSize, UIColor, self)
1028 DrawLeftText(": Create / Delete rows or columns", 150 + t_panel_width,
1029 yHelp - Help_Interval * 6, Help_FontSize, None, self)
1030 DrawLeftText("[" + context.scene.Key_Gapy + "][" + context.scene.Key_Gapx + "]",
1031 xHelp, yHelp - Help_Interval * 7, Help_FontSize, UIColor, self)
1032 DrawLeftText(": Gap between rows or columns", 150 + t_panel_width,
1033 yHelp - Help_Interval * 7, Help_FontSize, None, self)
1035 # Opengl Initialize
1036 bgl.glEnable(bgl.GL_BLEND)
1037 bgl.glColor4f(0.512, 0.919, 0.04, 1.0)
1038 bgl.glLineWidth(2)
1040 bgl.glEnable(bgl.GL_POINT_SMOOTH)
1041 bgl.glPointSize(6)
1043 if self.ProfileMode:
1044 xrect = region.width - t_panel_width - 80
1045 yrect = 80
1046 bgl.glColor4f(0.0, 0.0, 0.0, 0.3)
1047 bgl.glRecti(xrect, yrect, xrect + 60, yrect - 60)
1049 faces = self.Profils[self.nProfil][3]
1050 vertices = self.Profils[self.nProfil][2]
1051 WidthProfil = 50
1052 location = Vector((region.width - t_panel_width - WidthProfil, 50, 0))
1053 ProfilScale = 20.0
1054 bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.7)
1055 for f in faces:
1056 if len(f) == 4:
1057 bgl.glBegin(bgl.GL_QUADS)
1058 bgl.glVertex3f(vertices[f[0]][0] * ProfilScale + location.x, vertices[f[0]][1] *
1059 ProfilScale + location.y, vertices[f[0]][2] * ProfilScale + location.z)
1060 bgl.glVertex3f(vertices[f[1]][0] * ProfilScale + location.x, vertices[f[1]][1] *
1061 ProfilScale + location.y, vertices[f[1]][2] * ProfilScale + location.z)
1062 bgl.glVertex3f(vertices[f[2]][0] * ProfilScale + location.x, vertices[f[2]][1] *
1063 ProfilScale + location.y, vertices[f[2]][2] * ProfilScale + location.z)
1064 bgl.glVertex3f(vertices[f[3]][0] * ProfilScale + location.x, vertices[f[3]][1] *
1065 ProfilScale + location.y, vertices[f[3]][2] * ProfilScale + location.z)
1066 bgl.glEnd()
1067 if len(f) == 3:
1068 bgl.glBegin(bgl.GL_TRIANGLES)
1069 bgl.glVertex3f(vertices[f[0]][0] * ProfilScale + location.x, vertices[f[0]][1] *
1070 ProfilScale + location.y, vertices[f[0]][2] * ProfilScale + location.z)
1071 bgl.glVertex3f(vertices[f[1]][0] * ProfilScale + location.x, vertices[f[1]][1] *
1072 ProfilScale + location.y, vertices[f[1]][2] * ProfilScale + location.z)
1073 bgl.glVertex3f(vertices[f[2]][0] * ProfilScale + location.x, vertices[f[2]][1] *
1074 ProfilScale + location.y, vertices[f[2]][2] * ProfilScale + location.z)
1075 bgl.glEnd()
1077 if self.bDone:
1078 if len(self.mouse_path) > 1:
1079 x0 = self.mouse_path[0][0]
1080 y0 = self.mouse_path[0][1]
1081 x1 = self.mouse_path[1][0]
1082 y1 = self.mouse_path[1][1]
1084 # Cut Line
1085 if self.CutMode == LINE:
1086 if (self.shift) or (self.CreateMode and self.Closed):
1087 bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.5)
1089 bgl.glBegin(bgl.GL_POLYGON)
1090 for x, y in self.mouse_path:
1091 bgl.glVertex2i(x + self.xpos, y + self.ypos)
1092 bgl.glEnd()
1094 bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 1.0)
1095 bgl.glBegin(bgl.GL_LINE_STRIP)
1096 for x, y in self.mouse_path:
1097 bgl.glVertex2i(x + self.xpos, y + self.ypos)
1098 bgl.glEnd()
1099 if (self.CreateMode is False) or (self.CreateMode and self.Closed):
1100 bgl.glBegin(bgl.GL_LINE_STRIP)
1101 bgl.glVertex2i(self.mouse_path[len(self.mouse_path) - 1][0] + self.xpos,
1102 self.mouse_path[len(self.mouse_path) - 1][1] + self.ypos)
1103 bgl.glVertex2i(self.mouse_path[0][0] + self.xpos, self.mouse_path[0][1] + self.ypos)
1104 bgl.glEnd()
1106 bgl.glPointSize(6)
1107 bgl.glBegin(bgl.GL_POINTS)
1108 for x, y in self.mouse_path:
1109 bgl.glVertex2i(x + self.xpos, y + self.ypos)
1110 bgl.glEnd()
1112 # Cut rectangle
1113 if self.CutMode == RECTANGLE:
1114 bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.5)
1116 # if SHIFT, fill primitive
1117 if self.shift or self.CreateMode:
1118 bgl.glBegin(bgl.GL_QUADS)
1119 bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos)
1120 bgl.glVertex2i(x1 + self.xpos, y0 + self.ypos)
1121 bgl.glVertex2i(x1 + self.xpos, y1 + self.ypos)
1122 bgl.glVertex2i(x0 + self.xpos, y1 + self.ypos)
1123 bgl.glEnd()
1125 bgl.glBegin(bgl.GL_LINE_STRIP)
1126 bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos)
1127 bgl.glVertex2i(x1 + self.xpos, y0 + self.ypos)
1128 bgl.glVertex2i(x1 + self.xpos, y1 + self.ypos)
1129 bgl.glVertex2i(x0 + self.xpos, y1 + self.ypos)
1130 bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos)
1131 bgl.glEnd()
1132 bgl.glPointSize(6)
1134 bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 1.0)
1135 bgl.glBegin(bgl.GL_POINTS)
1136 bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos)
1137 bgl.glVertex2i(x1 + self.xpos, y0 + self.ypos)
1138 bgl.glVertex2i(x1 + self.xpos, y1 + self.ypos)
1139 bgl.glVertex2i(x0 + self.xpos, y1 + self.ypos)
1140 bgl.glEnd()
1142 # Circle Cut
1143 if self.CutMode == CIRCLE:
1144 DEG2RAD = 3.14159 / 180
1145 v0 = Vector((self.mouse_path[0][0], self.mouse_path[0][1], 0))
1146 v1 = Vector((self.mouse_path[1][0], self.mouse_path[1][1], 0))
1147 v0 -= v1
1148 radius = self.mouse_path[1][0] - self.mouse_path[0][0]
1149 DEG2RAD = 3.14159 / (180.0 / self.stepAngle[self.step])
1150 if self.ctrl:
1151 self.stepR = (self.mouse_path[1][1] - self.mouse_path[0][1]) / 25
1152 shift = (3.14159 / (360.0 / 60.0)) * int(self.stepR)
1153 else:
1154 shift = (self.mouse_path[1][1] - self.mouse_path[0][1]) / 50
1156 if self.shift or self.CreateMode:
1157 bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.5)
1158 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
1159 bgl.glVertex2f(x0 + self.xpos, y0 + self.ypos)
1160 for i in range(0, int(360 / self.stepAngle[self.step])):
1161 degInRad = i * DEG2RAD
1162 bgl.glVertex2f(x0 + self.xpos + math.cos(degInRad + shift) * radius,
1163 y0 + self.ypos + math.sin(degInRad + shift) * radius)
1164 bgl.glVertex2f(x0 + self.xpos + math.cos(0 + shift) * radius,
1165 y0 + self.ypos + math.sin(0 + shift) * radius)
1166 bgl.glEnd()
1168 bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 1.0)
1169 bgl.glBegin(bgl.GL_LINE_LOOP)
1170 for i in range(0, int(360 / self.stepAngle[self.step])):
1171 degInRad = i * DEG2RAD
1172 bgl.glVertex2f(x0 + self.xpos + math.cos(degInRad + shift) * radius,
1173 y0 + self.ypos + math.sin(degInRad + shift) * radius)
1174 bgl.glEnd()
1176 if self.ObjectMode or self.ProfileMode:
1177 if self.ShowCursor:
1178 region = context.region
1179 rv3d = context.space_data.region_3d
1181 if self.ObjectMode:
1182 ob = self.ObjectBrush
1183 if self.ProfileMode:
1184 ob = self.ProfileBrush
1185 mat = ob.matrix_world
1187 # 50% alpha, 2 pixel width line
1188 bgl.glEnable(bgl.GL_BLEND)
1190 bbox = [mat * Vector(b) for b in ob.bound_box]
1192 if self.shift:
1193 bgl.glLineWidth(4)
1194 bgl.glColor4f(0.5, 1.0, 0.0, 1.0)
1195 else:
1196 bgl.glLineWidth(2)
1197 bgl.glColor4f(1.0, 0.8, 0.0, 1.0)
1198 bgl.glBegin(bgl.GL_LINE_STRIP)
1199 idx = 0
1200 CRadius = ((bbox[7] - bbox[0]).length) / 2
1201 for i in range(int(len(self.CLR_C) / 3)):
1202 vector3d = (self.CLR_C[idx * 3] * CRadius + self.CurLoc.x, self.CLR_C[idx * 3 + 1] *
1203 CRadius + self.CurLoc.y, self.CLR_C[idx * 3 + 2] * CRadius + self.CurLoc.z)
1204 vector2d = bpy_extras.view3d_utils.location_3d_to_region_2d(region, rv3d, vector3d)
1205 if vector2d is not None:
1206 bgl.glVertex2f(*vector2d)
1207 idx += 1
1208 bgl.glEnd()
1210 bgl.glLineWidth(1)
1211 bgl.glDisable(bgl.GL_BLEND)
1212 bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
1214 # Object display
1215 if self.qRot is not None:
1216 ob.location = self.CurLoc
1217 v = Vector()
1218 v.x = v.y = 0.0
1219 v.z = self.BrushDepthOffset
1220 ob.location += self.qRot * v
1222 e = Euler()
1223 e.x = 0.0
1224 e.y = 0.0
1225 e.z = self.aRotZ / 25.0
1227 qe = e.to_quaternion()
1228 qRot = self.qRot * qe
1229 ob.rotation_mode = 'QUATERNION'
1230 ob.rotation_quaternion = qRot
1231 ob.rotation_mode = 'XYZ'
1233 if self.ProfileMode:
1234 if self.ProfileBrush is not None:
1235 self.ProfileBrush.location = self.CurLoc
1236 self.ProfileBrush.rotation_mode = 'QUATERNION'
1237 self.ProfileBrush.rotation_quaternion = qRot
1238 self.ProfileBrush.rotation_mode = 'XYZ'
1240 # Opengl defaults
1241 bgl.glLineWidth(1)
1242 bgl.glDisable(bgl.GL_BLEND)
1243 bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
1244 bgl.glDisable(bgl.GL_POINT_SMOOTH)
1247 # Intersection
1248 # intersection function (ideasman42)
1249 def isect_line_plane_v3(p0, p1, p_co, p_no, epsilon=1e-6):
1251 p0, p1: define the line
1252 p_co, p_no: define the plane:
1253 p_co is a point on the plane (plane coordinate).
1254 p_no is a normal vector defining the plane direction; does not need to be normalized.
1256 return a Vector or None (when the intersection can't be found).
1259 u = sub_v3v3(p1, p0)
1260 dot = dot_v3v3(p_no, u)
1262 if abs(dot) > epsilon:
1263 # the factor of the point between p0 -> p1 (0 - 1)
1264 # if 'fac' is between (0 - 1) the point intersects with the segment.
1265 # otherwise:
1266 # < 0.0: behind p0.
1267 # > 1.0: infront of p1.
1268 w = sub_v3v3(p0, p_co)
1269 fac = -dot_v3v3(p_no, w) / dot
1270 u = mul_v3_fl(u, fac)
1271 return add_v3v3(p0, u)
1272 else:
1273 # The segment is parallel to plane
1274 return None
1277 # ----------------------
1278 # generic math functions
1280 def add_v3v3(v0, v1):
1281 return (
1282 v0[0] + v1[0],
1283 v0[1] + v1[1],
1284 v0[2] + v1[2],
1288 def sub_v3v3(v0, v1):
1289 return (
1290 v0[0] - v1[0],
1291 v0[1] - v1[1],
1292 v0[2] - v1[2],
1296 def dot_v3v3(v0, v1):
1297 return (
1298 (v0[0] * v1[0]) +
1299 (v0[1] * v1[1]) +
1300 (v0[2] * v1[2])
1304 def len_squared_v3(v0):
1305 return dot_v3v3(v0, v0)
1308 def mul_v3_fl(v0, f):
1309 return (
1310 v0[0] * f,
1311 v0[1] * f,
1312 v0[2] * f,
1316 # Cut Square
1317 def CreateCutSquare(self, context):
1318 FAR_LIMIT = 10000.0
1320 # New mesh
1321 me = bpy.data.meshes.new('CMT_Square')
1322 # New object
1323 ob = bpy.data.objects.new('CMT_Square', me)
1324 # Save new object
1325 self.CurrentObj = ob
1327 # Scene information
1328 region = context.region
1329 rv3d = context.region_data
1330 coord = self.mouse_path[0][0], self.mouse_path[0][1]
1332 depthLocation = region_2d_to_vector_3d(region, rv3d, coord)
1333 self.ViewVector = depthLocation
1334 if self.snapCursor:
1335 PlanePoint = context.scene.cursor.location
1336 else:
1337 PlanePoint = self.OpsObj.location if self.OpsObj is not None else Vector((0.0, 0.0, 0.0))
1339 PlaneNormal = depthLocation
1340 PlaneNormalised = PlaneNormal.normalized()
1342 # Link object to scene
1343 context.collection.objects.link(ob)
1345 # New bmesh
1346 t_bm = bmesh.new()
1347 t_bm.from_mesh(me)
1348 # Convert in 3d space
1349 v0 = self.mouse_path[0][0] + self.xpos, self.mouse_path[0][1] + self.ypos
1350 v1 = self.mouse_path[1][0] + self.xpos, self.mouse_path[1][1] + self.ypos
1351 v2 = self.mouse_path[1][0] + self.xpos, self.mouse_path[0][1] + self.ypos
1352 v3 = self.mouse_path[0][0] + self.xpos, self.mouse_path[1][1] + self.ypos
1353 vec = region_2d_to_vector_3d(region, rv3d, v0)
1354 loc0 = region_2d_to_location_3d(region, rv3d, v0, vec)
1356 vec = region_2d_to_vector_3d(region, rv3d, v1)
1357 loc1 = region_2d_to_location_3d(region, rv3d, v1, vec)
1359 vec = region_2d_to_vector_3d(region, rv3d, v2)
1360 loc2 = region_2d_to_location_3d(region, rv3d, v2, vec)
1362 vec = region_2d_to_vector_3d(region, rv3d, v3)
1363 loc3 = region_2d_to_location_3d(region, rv3d, v3, vec)
1364 p0 = loc0
1365 p1 = loc0 + PlaneNormalised * FAR_LIMIT
1366 loc0 = isect_line_plane_v3(p0, p1, PlanePoint, PlaneNormalised)
1367 p0 = loc1
1368 p1 = loc1 + PlaneNormalised * FAR_LIMIT
1369 loc1 = isect_line_plane_v3(p0, p1, PlanePoint, PlaneNormalised)
1370 p0 = loc2
1371 p1 = loc2 + PlaneNormalised * FAR_LIMIT
1372 loc2 = isect_line_plane_v3(p0, p1, PlanePoint, PlaneNormalised)
1373 p0 = loc3
1374 p1 = loc3 + PlaneNormalised * FAR_LIMIT
1375 loc3 = isect_line_plane_v3(p0, p1, PlanePoint, PlaneNormalised)
1377 t_v0 = t_bm.verts.new(loc0)
1378 t_v1 = t_bm.verts.new(loc2)
1379 t_v2 = t_bm.verts.new(loc1)
1380 t_v3 = t_bm.verts.new(loc3)
1382 # Update vertices index
1383 t_bm.verts.index_update()
1384 # New faces
1385 t_face = t_bm.faces.new([t_v0, t_v1, t_v2, t_v3])
1386 # Set mesh
1387 t_bm.to_mesh(me)
1390 # Cut Line
1391 def CreateCutLine(self, context):
1392 FAR_LIMIT = 10000.0
1394 me = bpy.data.meshes.new('CMT_Line')
1396 ob = bpy.data.objects.new('CMT_Line', me)
1397 self.CurrentObj = ob
1399 region = context.region
1400 rv3d = context.region_data
1401 coord = self.mouse_path[0][0], self.mouse_path[0][1]
1402 depthLocation = region_2d_to_vector_3d(region, rv3d, coord)
1403 self.ViewVector = depthLocation
1405 PlanePoint = context.scene.cursor.location if self.snapCursor else Vector((0.0, 0.0, 0.0))
1406 PlaneNormal = depthLocation
1407 PlaneNormalised = PlaneNormal.normalized()
1409 context.collection.objects.link(ob)
1411 t_bm = bmesh.new()
1412 t_bm.from_mesh(me)
1414 FacesList = []
1415 NbVertices = 0
1417 bLine = False
1419 if (len(self.mouse_path) == 2) or ((len(self.mouse_path) <= 3) and
1420 (self.mouse_path[1] == self.mouse_path[2])):
1421 PlanePoint = Vector((0.0, 0.0, 0.0))
1422 PlaneNormal = depthLocation
1423 PlaneNormalised = PlaneNormal.normalized()
1424 # Force rebool
1425 self.ForceRebool = True
1426 # It's a line
1427 bLine = True
1428 Index = 0
1429 for x, y in self.mouse_path:
1430 if Index < 2:
1431 v0 = x + self.xpos, y + self.ypos
1432 vec = region_2d_to_vector_3d(region, rv3d, v0)
1433 loc0 = region_2d_to_location_3d(region, rv3d, v0, vec)
1435 p0 = loc0
1436 p1 = loc0 + PlaneNormalised * FAR_LIMIT
1437 loc0 = isect_line_plane_v3(p0, p1, PlanePoint, PlaneNormalised)
1439 NbVertices += 1
1440 Index += 1
1441 if NbVertices == 1:
1442 t_v0 = t_bm.verts.new(loc0)
1443 LocInit = loc0
1444 t_bm.verts.index_update()
1445 else:
1446 t_v1 = t_bm.verts.new(loc0)
1447 t_edges = t_bm.edges.new([t_v0, t_v1])
1448 NbVertices = 1
1449 t_v0 = t_v1
1450 else:
1451 for x, y in self.mouse_path:
1452 v0 = x + self.xpos, y + self.ypos
1453 vec = region_2d_to_vector_3d(region, rv3d, v0)
1454 loc0 = region_2d_to_location_3d(region, rv3d, v0, vec)
1456 p0 = loc0
1457 p1 = loc0 + PlaneNormalised * FAR_LIMIT
1458 loc0 = isect_line_plane_v3(p0, p1, PlanePoint, PlaneNormalised)
1460 NbVertices += 1
1461 if NbVertices == 1:
1462 t_v0 = t_bm.verts.new(loc0)
1463 LocInit = loc0
1464 t_bm.verts.index_update()
1465 FacesList.append(t_v0)
1466 else:
1467 t_v1 = t_bm.verts.new(loc0)
1468 t_edges = t_bm.edges.new([t_v0, t_v1])
1469 FacesList.append(t_v1)
1470 NbVertices = 1
1471 t_v0 = t_v1
1473 if self.CreateMode:
1474 if self.Closed and (bLine is False):
1475 t_v1 = t_bm.verts.new(LocInit)
1476 t_edges = t_bm.edges.new([t_v0, t_v1])
1477 FacesList.append(t_v1)
1478 t_face = t_bm.faces.new(FacesList)
1479 else:
1480 if bLine is False:
1481 t_v1 = t_bm.verts.new(LocInit)
1482 t_edges = t_bm.edges.new([t_v0, t_v1])
1483 FacesList.append(t_v1)
1484 t_face = t_bm.faces.new(FacesList)
1486 t_bm.to_mesh(me)
1489 # Cut Circle
1490 def CreateCutCircle(self, context):
1491 FAR_LIMIT = 10000.0
1493 me = bpy.data.meshes.new('CMT_Circle')
1495 ob = bpy.data.objects.new('CMT_Circle', me)
1496 self.CurrentObj = ob
1498 region = context.region
1499 rv3d = context.region_data
1500 coord = self.mouse_path[0][0], self.mouse_path[0][1]
1501 depthLocation = region_2d_to_vector_3d(region, rv3d, coord)
1502 self.ViewVector = depthLocation
1504 PlanePoint = context.scene.cursor.location if self.snapCursor else Vector((0.0, 0.0, 0.0))
1505 PlaneNormal = depthLocation
1506 PlaneNormalised = PlaneNormal.normalized()
1508 context.collection.objects.link(ob)
1510 t_bm = bmesh.new()
1511 t_bm.from_mesh(me)
1513 x0 = self.mouse_path[0][0]
1514 y0 = self.mouse_path[0][1]
1516 v0 = Vector((self.mouse_path[0][0], self.mouse_path[0][1], 0))
1517 v1 = Vector((self.mouse_path[1][0], self.mouse_path[1][1], 0))
1518 v0 -= v1
1519 radius = self.mouse_path[1][0] - self.mouse_path[0][0]
1520 DEG2RAD = math.pi / (180.0 / self.stepAngle[self.step])
1521 if self.ctrl:
1522 self.stepR = (self.mouse_path[1][1] - self.mouse_path[0][1]) / 25
1523 shift = (math.pi / (360.0 / self.stepAngle[self.step])) * (self.stepR)
1524 else:
1525 shift = (self.mouse_path[1][1] - self.mouse_path[0][1]) / 50
1527 # Convert point in 3D Space
1528 FacesList = []
1529 for i in range(0, int(360.0 / self.stepAngle[self.step])):
1530 degInRad = i * DEG2RAD
1531 v0 = x0 + self.xpos + math.cos(degInRad + shift) * radius, \
1532 y0 + self.ypos + math.sin(degInRad + shift) * radius
1533 vec = region_2d_to_vector_3d(region, rv3d, v0)
1534 loc0 = region_2d_to_location_3d(region, rv3d, v0, vec)
1536 p0 = loc0
1537 p1 = loc0 + PlaneNormalised * FAR_LIMIT
1538 loc0 = isect_line_plane_v3(p0, p1, PlanePoint, PlaneNormalised)
1540 t_v0 = t_bm.verts.new(loc0)
1542 FacesList.append(t_v0)
1544 t_bm.verts.index_update()
1545 t_face = t_bm.faces.new(FacesList)
1547 t_bm.to_mesh(me)
1550 # Object dimensions (SCULPT Tools tips)
1551 def objDiagonal(obj):
1552 return ((obj.dimensions[0]**2) + (obj.dimensions[1]**2) + (obj.dimensions[2]**2))**0.5
1555 # Bevel Update
1556 def update_bevel(context):
1557 selection = context.selected_objects.copy()
1558 active = context.active_object
1560 if len(selection) > 0:
1561 for obj in selection:
1562 bpy.ops.object.select_all(action='DESELECT')
1563 obj.select_set(True)
1564 context.view_layer.objects.active = obj
1566 # Test object name
1567 if obj.data.name.startswith("S_") or obj.data.name.startswith("S "):
1568 bpy.ops.object.mode_set(mode='EDIT')
1569 bpy.ops.mesh.region_to_loop()
1570 bpy.ops.transform.edge_bevelweight(value=1)
1571 bpy.ops.object.mode_set(mode='OBJECT')
1572 else:
1573 act_bevel = False
1574 for mod in obj.modifiers:
1575 if mod.type == 'BEVEL':
1576 act_bevel = True
1577 if act_bevel:
1578 context.view_layer.objects.active = bpy.data.objects[obj.name]
1579 active = obj
1581 bpy.ops.object.mode_set(mode='EDIT')
1583 # Edge mode
1584 bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
1586 # Clear all
1587 bpy.ops.mesh.select_all(action='SELECT')
1588 bpy.ops.mesh.mark_sharp(clear=True)
1589 bpy.ops.transform.edge_crease(value=-1)
1591 bpy.ops.transform.edge_bevelweight(value=-1)
1592 bpy.ops.mesh.select_all(action='DESELECT')
1593 bpy.ops.mesh.edges_select_sharp(sharpness=0.523599)
1594 bpy.ops.mesh.mark_sharp()
1595 bpy.ops.transform.edge_crease(value=1)
1596 bpy.ops.mesh.select_all(action='DESELECT')
1597 bpy.ops.mesh.edges_select_sharp(sharpness=0.523599)
1598 bpy.ops.transform.edge_bevelweight(value=1)
1599 bpy.ops.mesh.select_all(action='DESELECT')
1601 bpy.ops.object.mode_set(mode="OBJECT")
1603 active.data.use_customdata_edge_bevel = True
1605 for i in range(len(active.data.edges)):
1606 if active.data.edges[i].select is True:
1607 active.data.edges[i].bevel_weight = 1.0
1608 active.data.edges[i].use_edge_sharp = True
1610 Already = False
1611 for m in active.modifiers:
1612 if m.name == 'Bevel':
1613 Already = True
1615 if Already is False:
1616 bpy.ops.object.modifier_add(type='BEVEL')
1617 mod = context.object.modifiers[-1]
1618 mod.limit_method = 'WEIGHT'
1619 mod.width = 0.01
1620 mod.profile = 0.699099
1621 mod.use_clight_overlap = False
1622 mod.segments = 3
1623 mod.loop_slide = False
1625 bpy.ops.object.shade_smooth()
1627 context.object.data.use_auto_smooth = True
1628 context.object.data.auto_smooth_angle = 1.0472
1630 bpy.ops.object.select_all(action='DESELECT')
1632 for obj in selection:
1633 obj.select_set(True)
1634 context.view_layer.objects.active = active
1637 # Create bevel
1638 def CreateBevel(context, CurrentObject):
1639 # Save active object
1640 SavActive = context.active_object
1641 # Active "CurrentObject"
1642 context.view_layer.objects.active = CurrentObject
1644 bpy.ops.object.mode_set(mode='EDIT')
1646 # Edge mode
1647 bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
1649 # Clear all
1650 bpy.ops.mesh.select_all(action='SELECT')
1651 bpy.ops.mesh.mark_sharp(clear=True)
1652 bpy.ops.transform.edge_crease(value=-1)
1654 bpy.ops.transform.edge_bevelweight(value=-1)
1655 bpy.ops.mesh.select_all(action='DESELECT')
1656 bpy.ops.mesh.edges_select_sharp(sharpness=0.523599)
1657 bpy.ops.mesh.mark_sharp()
1658 bpy.ops.transform.edge_crease(value=1)
1659 bpy.ops.mesh.select_all(action='DESELECT')
1660 bpy.ops.mesh.edges_select_sharp(sharpness=0.523599)
1661 bpy.ops.transform.edge_bevelweight(value=1)
1662 bpy.ops.mesh.select_all(action='DESELECT')
1664 bpy.ops.object.mode_set(mode="OBJECT")
1666 bpy.ops.object.mode_set(mode='OBJECT')
1668 Already = False
1669 for m in CurrentObject.modifiers:
1670 if m.name == 'Bevel':
1671 Already = True
1673 if Already is False:
1674 bpy.ops.object.modifier_add(type='BEVEL')
1675 mod = context.object.modifiers[-1]
1676 mod.limit_method = 'WEIGHT'
1677 mod.width = 0.01
1678 mod.profile = 0.699099
1679 mod.use_clight_overlap = False
1680 mod.segments = 3
1681 mod.loop_slide = False
1683 bpy.ops.object.shade_smooth()
1685 context.object.data.use_auto_smooth = True
1686 context.object.data.auto_smooth_angle = 1.0471975
1688 # Restore the active object
1689 context.view_layer.objects.active = SavActive
1692 # Picking (template)
1693 def Picking(context, event):
1694 # get the context arguments
1695 scene = context.scene
1696 region = context.region
1697 rv3d = context.region_data
1698 coord = event.mouse_region_x, event.mouse_region_y
1700 # get the ray from the viewport and mouse
1701 view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord)
1702 ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord)
1703 ray_target = ray_origin + view_vector
1705 def visible_objects_and_duplis():
1706 for obj in context.visible_objects:
1707 if obj.type == 'MESH':
1708 yield (obj, obj.matrix_world.copy())
1710 if obj.instance_type != 'NONE':
1711 obj.dupli_list_create(scene)
1712 for dob in obj.dupli_list:
1713 obj_dupli = dob.object
1714 if obj_dupli.type == 'MESH':
1715 yield (obj_dupli, dob.matrix.copy())
1717 obj.dupli_list_clear()
1719 def obj_ray_cast(obj, matrix):
1720 # get the ray relative to the object
1721 matrix_inv = matrix.inverted()
1722 ray_origin_obj = matrix_inv * ray_origin
1723 ray_target_obj = matrix_inv * ray_target
1724 ray_direction_obj = ray_target_obj - ray_origin_obj
1725 # cast the ray
1726 success, location, normal, face_index = obj.ray_cast(ray_origin_obj, ray_direction_obj)
1727 if success:
1728 return location, normal, face_index
1729 return None, None, None
1731 # cast rays and find the closest object
1732 best_length_squared = -1.0
1733 best_obj = None
1735 # cast rays and find the closest object
1736 for obj, matrix in visible_objects_and_duplis():
1737 if obj.type == 'MESH':
1738 hit, normal, face_index = obj_ray_cast(obj, matrix)
1739 if hit is not None:
1740 hit_world = matrix * hit
1741 length_squared = (hit_world - ray_origin).length_squared
1742 if best_obj is None or length_squared < best_length_squared:
1743 scene.cursor.location = hit_world
1744 best_length_squared = length_squared
1745 best_obj = obj
1746 else:
1747 if best_obj is None:
1748 depthLocation = region_2d_to_vector_3d(region, rv3d, coord)
1749 loc = region_2d_to_location_3d(region, rv3d, coord, depthLocation)
1750 scene.cursor.location = loc
1753 def CreatePrimitive(self, _AngleStep, _radius):
1754 Angle = 0.0
1755 self.NbPointsInPrimitive = 0
1756 while(Angle < 360.0):
1757 self.CircleListRaw.append(math.cos(math.radians(Angle)) * _radius)
1758 self.CircleListRaw.append(math.sin(math.radians(Angle)) * _radius)
1759 self.CircleListRaw.append(0.0)
1760 Angle += _AngleStep
1761 self.NbPointsInPrimitive += 1
1762 self.CircleListRaw.append(math.cos(math.radians(0.0)) * _radius)
1763 self.CircleListRaw.append(math.sin(math.radians(0.0)) * _radius)
1764 self.CircleListRaw.append(0.0)
1765 self.NbPointsInPrimitive += 1
1768 def MoveCursor(qRot, location, self):
1769 if qRot is not None:
1770 self.CLR_C.clear()
1771 vc = Vector()
1772 idx = 0
1773 for i in range(int(len(self.CircleListRaw) / 3)):
1774 vc.x = self.CircleListRaw[idx * 3] * self.CRadius
1775 vc.y = self.CircleListRaw[idx * 3 + 1] * self.CRadius
1776 vc.z = self.CircleListRaw[idx * 3 + 2] * self.CRadius
1777 vc = qRot * vc
1778 self.CLR_C.append(vc.x)
1779 self.CLR_C.append(vc.y)
1780 self.CLR_C.append(vc.z)
1781 idx += 1
1784 def RBenVe(Object, Dir):
1785 ObjectV = Object.normalized()
1786 DirV = Dir.normalized()
1787 cosTheta = ObjectV.dot(DirV)
1788 rotationAxis = Vector((0.0, 0.0, 0.0))
1789 if (cosTheta < -1 + 0.001):
1790 v = Vector((0.0, 1.0, 0.0))
1791 rotationAxis = ObjectV.cross(v)
1792 rotationAxis = rotationAxis.normalized()
1793 q = Quaternion()
1794 q.w = 0.0
1795 q.x = rotationAxis.x
1796 q.y = rotationAxis.y
1797 q.z = rotationAxis.z
1798 return q
1799 rotationAxis = ObjectV.cross(DirV)
1800 s = math.sqrt((1.0 + cosTheta) * 2.0)
1801 invs = 1 / s
1802 q = Quaternion()
1803 q.w = s * 0.5
1804 q.x = rotationAxis.x * invs
1805 q.y = rotationAxis.y * invs
1806 q.z = rotationAxis.z * invs
1807 return q
1810 def Pick(context, event, self, ray_max=10000.0):
1811 region = context.region
1812 rv3d = context.region_data
1813 coord = event.mouse_region_x, event.mouse_region_y
1814 view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord)
1815 ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord)
1816 ray_target = ray_origin + (view_vector * ray_max)
1818 def obj_ray_cast(obj, matrix):
1819 matrix_inv = matrix.inverted()
1820 ray_origin_obj = matrix_inv * ray_origin
1821 ray_target_obj = matrix_inv * ray_target
1822 success, hit, normal, face_index = obj.ray_cast(ray_origin_obj, ray_target_obj)
1823 if success:
1824 return hit, normal, face_index
1825 return None, None, None
1827 best_length_squared = ray_max * ray_max
1828 best_obj = None
1829 for obj in self.CList:
1830 matrix = obj.matrix_world
1831 hit, normal, face_index = obj_ray_cast(obj, matrix)
1832 rotation = obj.rotation_euler.to_quaternion()
1833 if hit is not None:
1834 hit_world = matrix * hit
1835 length_squared = (hit_world - ray_origin).length_squared
1836 if length_squared < best_length_squared:
1837 best_length_squared = length_squared
1838 best_obj = obj
1839 hits = hit_world
1840 ns = normal
1841 fs = face_index
1843 if best_obj is not None:
1844 return hits, ns, fs, rotation
1846 return None, None, None
1849 def SelectObject(self, copyobj):
1850 copyobj.select_set(True)
1852 for child in copyobj.children:
1853 SelectObject(self, child)
1855 if copyobj.parent is None:
1856 bpy.context.view_layer.objects.active = copyobj
1859 # Undo
1860 def printUndo(self):
1861 for l in self.UList:
1862 print(l)
1865 def UndoAdd(self, type, OpsObj):
1866 if OpsObj is None:
1867 return
1868 if type != "DUPLICATE":
1869 ob = OpsObj
1870 # Create the 'backup' mesh
1871 bm = bmesh.new()
1872 bm.from_mesh(ob.data)
1874 self.UndoOps.append((OpsObj, type, bm))
1875 else:
1876 self.UndoOps.append((OpsObj, type, None))
1879 def UndoListUpdate(self):
1880 self.UList.append((self.UndoOps.copy()))
1881 self.UList_Index += 1
1882 self.UndoOps.clear()
1885 def Undo(self):
1886 if self.UList_Index < 0:
1887 return
1888 # get previous mesh
1889 for o in self.UList[self.UList_Index]:
1890 if o[1] == "MESH":
1891 bm = o[2]
1892 bm.to_mesh(o[0].data)
1894 SelectObjList = bpy.context.selected_objects.copy()
1895 Active_Obj = bpy.context.active_object
1896 bpy.ops.object.select_all(action='TOGGLE')
1898 for o in self.UList[self.UList_Index]:
1899 if o[1] == "REBOOL":
1900 o[0].select_set(True)
1901 o[0].hide = False
1903 if o[1] == "DUPLICATE":
1904 o[0].select_set(True)
1905 o[0].hide = False
1907 bpy.ops.object.delete(use_global=False)
1909 for so in SelectObjList:
1910 bpy.data.objects[so.name].select_set(True)
1911 bpy.context.view_layer.objects.active = Active_Obj
1913 self.UList_Index -= 1
1914 self.UList[self.UList_Index + 1:] = []
1917 def duplicateObject(self):
1918 if self.Instantiate:
1919 bpy.ops.object.duplicate_move_linked(
1920 OBJECT_OT_duplicate={
1921 "linked": True,
1922 "mode": 'TRANSLATION',
1924 TRANSFORM_OT_translate={
1925 "value": (0, 0, 0),
1928 else:
1929 bpy.ops.object.duplicate_move(
1930 OBJECT_OT_duplicate={
1931 "linked": False,
1932 "mode": 'TRANSLATION',
1934 TRANSFORM_OT_translate={
1935 "value": (0, 0, 0),
1939 ob_new = bpy.context.active_object
1941 ob_new.location = self.CurLoc
1942 v = Vector()
1943 v.x = v.y = 0.0
1944 v.z = self.BrushDepthOffset
1945 ob_new.location += self.qRot * v
1947 if self.ObjectMode:
1948 ob_new.scale = self.ObjectBrush.scale
1949 if self.ProfileMode:
1950 ob_new.scale = self.ProfileBrush.scale
1952 e = Euler()
1953 e.x = e.y = 0.0
1954 e.z = self.aRotZ / 25.0
1956 # If duplicate with a grid, no random rotation (each mesh in the grid is already rotated randomly)
1957 if (self.alt is True) and ((self.nbcol + self.nbrow) < 3):
1958 if self.RandomRotation:
1959 e.z += random.random()
1961 qe = e.to_quaternion()
1962 qRot = self.qRot * qe
1963 ob_new.rotation_mode = 'QUATERNION'
1964 ob_new.rotation_quaternion = qRot
1965 ob_new.rotation_mode = 'XYZ'
1967 if (ob_new.display_type == "WIRE") and (self.BrushSolidify is False):
1968 ob_new.hide = True
1970 if self.BrushSolidify:
1971 ob_new.display_type = "SOLID"
1972 ob_new.show_in_front = False
1974 for o in bpy.context.selected_objects:
1975 UndoAdd(self, "DUPLICATE", o)
1977 if len(bpy.context.selected_objects) > 0:
1978 bpy.ops.object.select_all(action='TOGGLE')
1979 for o in self.SavSel:
1980 o.select_set(True)
1982 bpy.context.view_layer.objects.active = self.OpsObj
1985 def update_grid(self, context):
1987 Thanks to batFINGER for his help :
1988 source : http://blender.stackexchange.com/questions/55864/multiple-meshes-not-welded-with-pydata
1990 verts = []
1991 edges = []
1992 faces = []
1993 numface = 0
1995 if self.nbcol < 1:
1996 self.nbcol = 1
1997 if self.nbrow < 1:
1998 self.nbrow = 1
1999 if self.gapx < 0:
2000 self.gapx = 0
2001 if self.gapy < 0:
2002 self.gapy = 0
2004 # Get the data from the profils or the object
2005 if self.ProfileMode:
2006 brush = bpy.data.objects.new(
2007 self.Profils[self.nProfil][0],
2008 bpy.data.meshes[self.Profils[self.nProfil][0]]
2010 obj = bpy.data.objects["CT_Profil"]
2011 obfaces = brush.data.polygons
2012 obverts = brush.data.vertices
2013 lenverts = len(obverts)
2014 else:
2015 brush = bpy.data.objects["CarverBrushCopy"]
2016 obj = context.selected_objects[0]
2017 obverts = brush.data.vertices
2018 obfaces = brush.data.polygons
2019 lenverts = len(brush.data.vertices)
2021 # Gap between each row / column
2022 gapx = self.gapx
2023 gapy = self.gapy
2025 # Width of each row / column
2026 widthx = brush.dimensions.x * self.scale_x
2027 widthy = brush.dimensions.y * self.scale_y
2029 # Compute the corners so the new object will be always at the center
2030 left = -((self.nbcol - 1) * (widthx + gapx)) / 2
2031 start = -((self.nbrow - 1) * (widthy + gapy)) / 2
2033 for i in range(self.nbrow * self.nbcol):
2034 row = i % self.nbrow
2035 col = i // self.nbrow
2036 startx = left + ((widthx + gapx) * col)
2037 starty = start + ((widthy + gapy) * row)
2039 # Add random rotation
2040 if (self.RandomRotation) and not (self.GridScaleX or self.GridScaleY):
2041 rotmat = Matrix.Rotation(math.radians(360 * random.random()), 4, 'Z')
2042 for v in obverts:
2043 v.co = v.co * rotmat
2045 verts.extend([((v.co.x - startx, v.co.y - starty, v.co.z)) for v in obverts])
2046 faces.extend([[v + numface * lenverts for v in p.vertices] for p in obfaces])
2047 numface += 1
2049 # Update the mesh
2050 # Create mesh data
2051 mymesh = bpy.data.meshes.new("CT_Profil")
2052 # Generate mesh data
2053 mymesh.from_pydata(verts, edges, faces)
2054 # Calculate the edges
2055 mymesh.update(calc_edges=True)
2056 # Update data
2057 obj.data = mymesh
2058 # Make the object active to remove doubles
2059 context.view_layer.objects.active = obj
2062 def boolean_operation(bool_type="DIFFERENCE"):
2063 ActiveObj = bpy.context.active_object
2064 sel_index = 0 if bpy.context.selected_objects[0] != bpy.context.active_object else 1
2066 bpy.ops.object.modifier_apply(apply_as='DATA', modifier="CT_SOLIDIFY")
2067 BoolMod = ActiveObj.modifiers.new(
2068 "CT_" + bpy.context.selected_objects[sel_index].name,
2069 "BOOLEAN"
2071 BoolMod.object = bpy.context.selected_objects[sel_index]
2072 BoolMod.operation = bool_type
2073 bpy.context.selected_objects[sel_index].display_type = 'WIRE'
2076 def Rebool(context, self):
2077 LastObj = context.active_object
2079 Brush = context.selected_objects[0]
2080 Brush.display_type = "WIRE"
2081 obj = context.selected_objects[1]
2083 bpy.ops.object.select_all(action='TOGGLE')
2085 context.view_layer.objects.active = obj
2086 obj.display_type = "SOLID"
2087 obj.select_set(True)
2088 bpy.ops.object.duplicate_move(
2089 OBJECT_OT_duplicate={
2090 "linked": False,
2091 "mode": 'TRANSLATION',
2093 TRANSFORM_OT_translate={
2094 "value": (0, 0, 0),
2095 "constraint_axis": (False, False, False),
2096 "orient_type": 'GLOBAL',
2097 "mirror": False,
2098 "proportional": 'DISABLED',
2099 "proportional_edit_falloff": 'SMOOTH',
2100 "proportional_size": 1,
2101 "snap": False,
2102 "snap_target": 'CLOSEST',
2103 "snap_point": (0, 0, 0),
2104 "snap_align": False,
2105 "snap_normal": (0, 0, 0),
2106 "gpencil_strokes": False,
2107 "texture_space": False,
2108 "remove_on_cancel": False,
2109 "release_confirm": False,
2112 LastObjectCreated = context.active_object
2114 m = LastObjectCreated.modifiers.new("CT_INTERSECT", "BOOLEAN")
2115 m.operation = "INTERSECT"
2116 m.object = Brush
2118 m = obj.modifiers.new("CT_DIFFERENCE", "BOOLEAN")
2119 m.operation = "DIFFERENCE"
2120 m.object = Brush
2122 for mb in LastObj.modifiers:
2123 if mb.type == 'BEVEL':
2124 mb.show_viewport = False
2126 if self.ObjectBrush or self.ProfileBrush:
2127 LastObjectCreated.show_in_front = False
2128 try:
2129 bpy.ops.object.modifier_apply(apply_as='DATA', modifier="CT_SOLIDIFY")
2130 except:
2131 exc_type, exc_value, exc_traceback = sys.exc_info()
2132 self.report({'ERROR'}, str(exc_value))
2134 if self.DontApply is False:
2135 try:
2136 bpy.ops.object.modifier_apply(apply_as='DATA', modifier="CT_INTERSECT")
2137 except:
2138 exc_type, exc_value, exc_traceback = sys.exc_info()
2139 self.report({'ERROR'}, str(exc_value))
2141 bpy.ops.object.select_all(action='TOGGLE')
2143 for mb in LastObj.modifiers:
2144 if mb.type == 'BEVEL':
2145 mb.show_viewport = True
2147 context.view_layer.objects.active = obj
2148 obj.select_set(True)
2149 if self.DontApply is False:
2150 try:
2151 bpy.ops.object.modifier_apply(apply_as='DATA', modifier="CT_DIFFERENCE")
2152 except:
2153 exc_type, exc_value, exc_traceback = sys.exc_info()
2154 self.report({'ERROR'}, str(exc_value))
2156 bpy.ops.object.select_all(action='TOGGLE')
2158 LastObjectCreated.select_set(True)
2161 def createMeshFromData(self):
2162 if self.Profils[self.nProfil][0] not in bpy.data.meshes:
2163 # Create mesh and object
2164 me = bpy.data.meshes.new(self.Profils[self.nProfil][0])
2165 # Create mesh from given verts, faces.
2166 me.from_pydata(self.Profils[self.nProfil][2], [], self.Profils[self.nProfil][3])
2167 # Update mesh with new data
2168 me.update()
2170 if "CT_Profil" not in bpy.data.objects:
2171 ob = bpy.data.objects.new("CT_Profil", bpy.data.meshes[self.Profils[self.nProfil][0]])
2172 ob.location = Vector((0.0, 0.0, 0.0))
2174 # Link object to scene and make active
2175 scn = bpy.context.scene
2176 scn.objects.link(ob)
2177 scn.objects.active = ob
2178 ob.select_set(True)
2179 ob.location = Vector((10000.0, 0.0, 0.0))
2180 ob.display_type = "WIRE"
2182 self.SolidifyPossible = True
2183 else:
2184 bpy.data.objects["CT_Profil"].data = bpy.data.meshes[self.Profils[self.nProfil][0]]
2187 def Selection_Save_Restore(self):
2188 if "CT_Profil" in bpy.data.objects:
2189 Selection_Save(self)
2190 bpy.ops.object.select_all(action='DESELECT')
2191 bpy.data.objects["CT_Profil"].select_set(True)
2192 bpy.context.view_layer.objects.active = bpy.data.objects["CT_Profil"]
2193 if bpy.data.objects["CT_Profil"] in self.SavSel:
2194 self.SavSel.remove(bpy.data.objects["CT_Profil"])
2195 bpy.ops.object.delete(use_global=False)
2196 Selection_Restore(self)
2199 def Selection_Save(self):
2200 obj_name = getattr(bpy.context.active_object, "name", None)
2201 self.SavSel = bpy.context.selected_objects.copy()
2202 self.Sav_ac = obj_name
2205 def Selection_Restore(self):
2206 for o in self.SavSel:
2207 o.select_set(True)
2208 if self.Sav_ac:
2209 bpy.context.view_layer.objects.active = bpy.data.objects.get(self.Sav_ac, None)
2212 # Modal Operator
2213 class Carver(bpy.types.Operator):
2214 bl_idname = "object.carver"
2215 bl_label = "Carver"
2216 bl_description = "Cut or create Meshes in Object mode"
2217 bl_options = {'REGISTER', 'UNDO'}
2219 @classmethod
2220 def poll(cls, context):
2221 ob = None
2222 if len(context.selected_objects) > 0:
2223 ob = context.selected_objects[0]
2224 # Test if selected object or none (for create mode)
2225 return (
2226 (ob and ob.type == 'MESH' and context.mode == 'OBJECT') or
2227 (context.mode == 'OBJECT' and ob is None) or
2228 (context.mode == 'EDIT_MESH'))
2230 def modal(self, context, event):
2231 PI = 3.14156
2232 region_types = {'WINDOW', 'UI'}
2233 win = context.window
2235 for area in win.screen.areas:
2236 if area.type in ('VIEW_3D'):
2237 for region in area.regions:
2238 if not region_types or region.type in region_types:
2239 region.tag_redraw()
2241 if event.type in {
2242 'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE',
2243 'NUMPAD_1', 'NUMPAD_2', 'NUMPAD_3', 'NUMPAD_4', 'NUMPAD_6',
2244 'NUMPAD_7', 'NUMPAD_8', 'NUMPAD_9', 'NUMPAD_5'}:
2245 return {'PASS_THROUGH'}
2246 try:
2247 # [Shift]
2248 self.shift = True if event.shift else False
2250 # [Ctrl]
2251 self.ctrl = True if event.ctrl else False
2253 # [Alt]
2254 self.alt = False
2255 if event.alt:
2256 if self.InitPosition is False:
2257 # Initialise position variable for start position
2258 self.xpos = 0
2259 self.ypos = 0
2260 self.last_mouse_region_x = event.mouse_region_x
2261 self.last_mouse_region_y = event.mouse_region_y
2262 self.InitPosition = True
2263 self.alt = True
2264 # [Alt] release
2265 if self.InitPosition and self.alt is False:
2266 # Update coordinates
2267 for i in range(0, len(self.mouse_path)):
2268 l = list(self.mouse_path[i])
2269 l[0] += self.xpos
2270 l[1] += self.ypos
2271 self.mouse_path[i] = tuple(l)
2273 self.xpos = self.ypos = 0
2274 self.InitPosition = False
2276 # Mode change (cut type)
2277 if event.type == 'SPACE' and event.value == 'PRESS':
2278 if self.ObjectMode or self.ProfileMode:
2279 # If grid, remove double with intersect meshes
2280 if ((self.nbcol + self.nbrow) > 3):
2281 # Go in edit mode mode
2282 bpy.ops.object.mode_set(mode='EDIT')
2283 # Remove duplicate vertices
2284 bpy.ops.mesh.remove_doubles()
2285 # Return in object mode
2286 bpy.ops.object.mode_set(mode='OBJECT')
2288 if self.alt:
2289 # Save selected objects
2290 self.SavSel = context.selected_objects.copy()
2291 if len(context.selected_objects) > 0:
2292 bpy.ops.object.select_all(action='TOGGLE')
2294 if self.ObjectMode:
2295 SelectObject(self, self.ObjectBrush)
2296 else:
2297 SelectObject(self, self.ProfileBrush)
2298 duplicateObject(self)
2299 else:
2300 # Brush Cut
2301 self.Cut()
2302 # Save selected objects
2303 if self.ObjectMode:
2304 if len(self.ObjectBrush.children) > 0:
2305 self.SavSel = context.selected_objects.copy()
2306 if len(context.selected_objects) > 0:
2307 bpy.ops.object.select_all(action='TOGGLE')
2309 if self.ObjectMode:
2310 SelectObject(self, self.ObjectBrush)
2311 else:
2312 SelectObject(self, self.ProfileBrush)
2313 duplicateObject(self)
2315 UndoListUpdate(self)
2317 # Save cursor position
2318 self.SavMousePos = self.CurLoc
2319 else:
2320 if self.bDone is False:
2321 # Cut Mode
2322 self.CutMode += 1
2323 if self.CutMode > 2:
2324 self.CutMode = 0
2325 else:
2326 if self.CutMode == LINE:
2327 # Cuts creation
2328 CreateCutLine(self, context)
2329 if self.CreateMode:
2330 # Object creation
2331 self.CreateGeometry()
2332 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
2333 # Cursor Snap
2334 context.scene.mesh_carver.DepthCursor = self.snapCursor
2335 # Object Instantiate
2336 context.scene.mesh_carver.OInstanciate = self.Instantiate
2337 # Random rotation
2338 context.scene.mesh_carver.ORandom = self.RandomRotation
2340 return {'FINISHED'}
2341 else:
2342 self.Cut()
2343 UndoListUpdate(self)
2345 # Object creation
2346 if event.type == context.scene.Key_Create and event.value == 'PRESS':
2347 if self.ExclusiveCreateMode is False:
2348 self.CreateMode = not self.CreateMode
2350 # Auto Bevel Update
2351 if event.type == context.scene.Key_Update and event.value == 'PRESS':
2352 self.Auto_BevelUpdate = not self.Auto_BevelUpdate
2354 # Boolean operation type
2355 if event.type == context.scene.Key_Bool and event.value == 'PRESS':
2356 if (self.ProfileMode is True) or (self.ObjectMode is True):
2357 if self.BoolOps == DIFFERENCE:
2358 self.BoolOps = UNION
2359 else:
2360 self.BoolOps = DIFFERENCE
2362 # Brush Mode
2363 if event.type == context.scene.Key_Brush and event.value == 'PRESS':
2364 self.DontApply = False
2365 if (self.ProfileMode is False) and (self.ObjectMode is False):
2366 self.ProfileMode = True
2367 else:
2368 self.ProfileMode = False
2369 if self.ObjectBrush is not None:
2370 if self.ObjectMode is False:
2371 self.ObjectMode = True
2372 self.BrushSolidify = False
2373 self.CList = self.OB_List
2375 Selection_Save_Restore(self)
2376 context.scene.mesh_carver.nProfile = self.nProfil
2377 else:
2378 self.ObjectMode = False
2379 else:
2380 self.BrushSolidify = False
2381 Selection_Save_Restore(self)
2383 if self.ProfileMode:
2384 createMeshFromData(self)
2385 self.ProfileBrush = bpy.data.objects["CT_Profil"]
2386 Selection_Save(self)
2387 self.BrushSolidify = True
2389 bpy.ops.object.select_all(action='TOGGLE')
2390 self.ProfileBrush.select_set(True)
2391 context.view_layer.objects.active = self.ProfileBrush
2392 # Set xRay
2393 self.ProfileBrush.show_in_front = True
2395 bpy.ops.object.modifier_add(type='SOLIDIFY')
2396 context.object.modifiers["Solidify"].name = "CT_SOLIDIFY"
2397 context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1
2399 Selection_Restore(self)
2401 self.CList = self.CurrentSelection
2402 else:
2403 if self.ObjectBrush is not None:
2404 if self.ObjectMode is False:
2405 if self.ObjectBrush is not None:
2406 self.ObjectBrush.location = self.InitBrushPosition
2407 self.ObjectBrush.scale = self.InitBrushScale
2408 self.ObjectBrush.rotation_quaternion = self.InitBrushQRotation
2409 self.ObjectBrush.rotation_euler = self.InitBrushERotation
2410 self.ObjectBrush.display_type = self.ObjectBrush_DT
2411 self.ObjectBrush.show_in_front = self.XRay
2413 # Remove solidify modifier
2414 Selection_Save(self)
2415 self.BrushSolidify = False
2417 bpy.ops.object.select_all(action='TOGGLE')
2418 self.ObjectBrush.select_set(True)
2419 context.view_layer.objects.active = self.ObjectBrush
2421 bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY")
2423 Selection_Restore(self)
2424 else:
2425 if self.Solidify_Active_Start:
2426 Selection_Save(self)
2427 self.BrushSolidify = True
2428 self.SolidifyPossible = True
2429 bpy.ops.object.select_all(action='TOGGLE')
2430 self.ObjectBrush.select_set(True)
2431 context.view_layer.objects.active = self.ObjectBrush
2432 # Set xRay
2433 self.ObjectBrush.show_in_front = True
2434 bpy.ops.object.modifier_add(type='SOLIDIFY')
2435 context.object.modifiers["Solidify"].name = "CT_SOLIDIFY"
2436 context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1
2437 Selection_Restore(self)
2439 # Help display
2440 if event.type == context.scene.Key_Help and event.value == 'PRESS':
2441 self.AskHelp = not self.AskHelp
2443 # Instantiate object
2444 if event.type == context.scene.Key_Instant and event.value == 'PRESS':
2445 self.Instantiate = not self.Instantiate
2447 # Close polygonal shape
2448 if event.type == context.scene.Key_Close and event.value == 'PRESS':
2449 if self.CreateMode:
2450 self.Closed = not self.Closed
2452 if event.type == context.scene.Key_Apply and event.value == 'PRESS':
2453 self.DontApply = not self.DontApply
2455 # Scale object
2456 if event.type == context.scene.Key_Scale and event.value == 'PRESS':
2457 if self.ObjectScale is False:
2458 self.am = event.mouse_region_x, event.mouse_region_y
2459 self.ObjectScale = True
2461 # Grid : Add column
2462 if event.type == 'UP_ARROW' and event.value == 'PRESS':
2463 self.nbcol += 1
2464 update_grid(self, context)
2466 # Grid : Add row
2467 elif event.type == 'RIGHT_ARROW' and event.value == 'PRESS':
2468 self.nbrow += 1
2469 update_grid(self, context)
2471 # Grid : Delete column
2472 elif event.type == 'DOWN_ARROW' and event.value == 'PRESS':
2473 self.nbcol -= 1
2474 update_grid(self, context)
2476 # Grid : Delete row
2477 elif event.type == 'LEFT_ARROW' and event.value == 'PRESS':
2478 self.nbrow -= 1
2479 update_grid(self, context)
2481 # Grid : Scale gap between columns
2482 if event.type == context.scene.Key_Gapy and event.value == 'PRESS':
2483 if self.GridScaleX is False:
2484 self.am = event.mouse_region_x, event.mouse_region_y
2485 self.GridScaleX = True
2487 # Grid : Scale gap between rows
2488 if event.type == context.scene.Key_Gapx and event.value == 'PRESS':
2489 if self.GridScaleY is False:
2490 self.am = event.mouse_region_x, event.mouse_region_y
2491 self.GridScaleY = True
2493 # Cursor depth or solidify pattern
2494 if event.type == context.scene.Key_Depth and event.value == 'PRESS':
2495 if (self.ObjectMode is False) and (self.ProfileMode is False):
2496 self.snapCursor = not self.snapCursor
2497 else:
2498 # Solidify
2500 if (self.ObjectMode or self.ProfileMode) and (self.SolidifyPossible):
2501 solidify = True
2503 if self.ObjectMode:
2504 z = self.ObjectBrush.data.vertices[0].co.z
2505 ErrorMarge = 0.01
2506 for v in self.ObjectBrush.data.vertices:
2507 if abs(v.co.z - z) > ErrorMarge:
2508 solidify = False
2509 self.CarveDepth = True
2510 self.am = event.mouse_region_x, event.mouse_region_y
2511 break
2513 if solidify:
2514 if self.ObjectMode:
2515 for mb in self.ObjectBrush.modifiers:
2516 if mb.type == 'SOLIDIFY':
2517 AlreadySoldify = True
2518 else:
2519 for mb in self.ProfileBrush.modifiers:
2520 if mb.type == 'SOLIDIFY':
2521 AlreadySoldify = True
2523 if AlreadySoldify is False:
2524 Selection_Save(self)
2525 self.BrushSolidify = True
2527 bpy.ops.object.select_all(action='TOGGLE')
2528 if self.ObjectMode:
2529 self.ObjectBrush.select_set(True)
2530 context.view_layer.objects.active = self.ObjectBrush
2531 # Active le xray
2532 self.ObjectBrush.show_in_front = True
2533 else:
2534 self.ProfileBrush.select_set(True)
2535 context.view_layer.objects.active = self.ProfileBrush
2536 # Active le xray
2537 self.ProfileBrush.show_in_front = True
2539 bpy.ops.object.modifier_add(type='SOLIDIFY')
2540 context.object.modifiers["Solidify"].name = "CT_SOLIDIFY"
2542 context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1
2544 Selection_Restore(self)
2546 self.WidthSolidify = not self.WidthSolidify
2547 self.am = event.mouse_region_x, event.mouse_region_y
2549 if event.type == context.scene.Key_BrushDepth and event.value == 'PRESS':
2550 if self.ObjectMode:
2551 self.CarveDepth = False
2553 self.BrushDepth = True
2554 self.am = event.mouse_region_x, event.mouse_region_y
2556 # Random rotation
2557 if event.type == 'R' and event.value == 'PRESS':
2558 self.RandomRotation = not self.RandomRotation
2560 # Undo
2561 if event.type == 'Z' and event.value == 'PRESS':
2562 if self.ctrl:
2563 if (self.CutMode == LINE) and (self.bDone):
2564 if len(self.mouse_path) > 1:
2565 self.mouse_path[len(self.mouse_path) - 1:] = []
2566 else:
2567 Undo(self)
2569 # Mouse move
2570 if event.type == 'MOUSEMOVE':
2572 if self.ObjectMode or self.ProfileMode:
2573 fac = 50.0
2574 if self.shift:
2575 fac = 500.0
2576 if self.WidthSolidify:
2577 if self.ObjectMode:
2578 bpy.data.objects[self.ObjectBrush.name].modifiers[
2579 "CT_SOLIDIFY"].thickness += (event.mouse_region_x - self.am[0]) / fac
2580 elif self.ProfileMode:
2581 bpy.data.objects[self.ProfileBrush.name].modifiers[
2582 "CT_SOLIDIFY"].thickness += (event.mouse_region_x - self.am[0]) / fac
2583 self.am = event.mouse_region_x, event.mouse_region_y
2584 elif self.CarveDepth:
2585 for v in self.ObjectBrush.data.vertices:
2586 v.co.z += (event.mouse_region_x - self.am[0]) / fac
2587 self.am = event.mouse_region_x, event.mouse_region_y
2588 elif self.BrushDepth:
2589 self.BrushDepthOffset += (event.mouse_region_x - self.am[0]) / fac
2590 self.am = event.mouse_region_x, event.mouse_region_y
2591 else:
2592 if (self.GridScaleX):
2593 self.gapx += (event.mouse_region_x - self.am[0]) / 50
2594 self.am = event.mouse_region_x, event.mouse_region_y
2595 update_grid(self, context)
2596 return {'RUNNING_MODAL'}
2598 elif (self.GridScaleY):
2599 self.gapy += (event.mouse_region_x - self.am[0]) / 50
2600 self.am = event.mouse_region_x, event.mouse_region_y
2601 update_grid(self, context)
2602 return {'RUNNING_MODAL'}
2604 elif self.ObjectScale:
2605 self.ascale = -(event.mouse_region_x - self.am[0])
2606 self.am = event.mouse_region_x, event.mouse_region_y
2608 if self.ObjectMode:
2609 self.ObjectBrush.scale.x -= float(self.ascale) / 150.0
2610 if self.ObjectBrush.scale.x <= 0.0:
2611 self.ObjectBrush.scale.x = 0.0
2612 self.ObjectBrush.scale.y -= float(self.ascale) / 150.0
2613 if self.ObjectBrush.scale.y <= 0.0:
2614 self.ObjectBrush.scale.y = 0.0
2615 self.ObjectBrush.scale.z -= float(self.ascale) / 150.0
2616 if self.ObjectBrush.scale.z <= 0.0:
2617 self.ObjectBrush.scale.z = 0.0
2619 elif self.ProfileMode:
2620 if self.ProfileBrush is not None:
2621 self.ProfileBrush.scale.x -= float(self.ascale) / 150.0
2622 self.ProfileBrush.scale.y -= float(self.ascale) / 150.0
2623 self.ProfileBrush.scale.z -= float(self.ascale) / 150.0
2624 else:
2625 if self.LMB:
2626 if self.ctrl:
2627 self.aRotZ = - \
2628 ((int((event.mouse_region_x - self.xSavMouse) / 10.0) * PI / 4.0) * 25.0)
2629 else:
2630 self.aRotZ -= event.mouse_region_x - self.am[0]
2631 self.ascale = 0.0
2633 self.am = event.mouse_region_x, event.mouse_region_y
2634 else:
2635 vBack = Pick(context, event, self)
2636 if vBack[0] is not None:
2637 self.ShowCursor = True
2638 NormalObject = Vector((0.0, 0.0, 1.0))
2639 qR = RBenVe(NormalObject, vBack[1])
2640 self.qRot = vBack[3] * qR
2641 MoveCursor(qR, vBack[0], self)
2642 self.SavCurLoc = vBack[0]
2643 if self.ctrl:
2644 if self.SavMousePos is not None:
2645 xEcart = abs(self.SavMousePos.x - self.SavCurLoc.x)
2646 yEcart = abs(self.SavMousePos.y - self.SavCurLoc.y)
2647 zEcart = abs(self.SavMousePos.z - self.SavCurLoc.z)
2648 if (xEcart > yEcart) and (xEcart > zEcart):
2649 self.CurLoc = Vector(
2650 (vBack[0].x, self.SavMousePos.y, self.SavMousePos.z))
2651 if (yEcart > xEcart) and (yEcart > zEcart):
2652 self.CurLoc = Vector(
2653 (self.SavMousePos.x, vBack[0].y, self.SavMousePos.z))
2654 if (zEcart > xEcart) and (zEcart > yEcart):
2655 self.CurLoc = Vector(
2656 (self.SavMousePos.x, self.SavMousePos.y, vBack[0].z))
2657 else:
2658 self.CurLoc = vBack[0]
2659 else:
2660 self.CurLoc = vBack[0]
2661 else:
2662 if self.bDone:
2663 if self.alt is False:
2664 if self.bDone:
2665 if self.ctrl and (self.CutMode == LINE):
2666 # Incremental mode
2667 coord = list(self.mouse_path[len(self.mouse_path) - 1])
2668 coord[0] = int(
2669 self.mouse_path[len(self.mouse_path) - 2][0] +
2670 int((event.mouse_region_x -
2671 self.mouse_path[len(self.mouse_path) - 2][0]
2672 ) / self.Increment) * self.Increment
2674 coord[1] = int(
2675 self.mouse_path[len(self.mouse_path) - 2][1] +
2676 int((event.mouse_region_y -
2677 self.mouse_path[len(self.mouse_path) - 2][1]
2678 ) / self.Increment) * self.Increment
2680 self.mouse_path[len(self.mouse_path) - 1] = tuple(coord)
2681 else:
2682 if len(self.mouse_path) > 0:
2683 self.mouse_path[len(self.mouse_path) -
2684 1] = (event.mouse_region_x, event.mouse_region_y)
2685 else:
2686 # [ALT] press, update position
2687 self.xpos += (event.mouse_region_x - self.last_mouse_region_x)
2688 self.ypos += (event.mouse_region_y - self.last_mouse_region_y)
2690 self.last_mouse_region_x = event.mouse_region_x
2691 self.last_mouse_region_y = event.mouse_region_y
2693 elif event.type == 'LEFTMOUSE' and event.value == 'PRESS':
2694 if self.ObjectMode or self.ProfileMode:
2695 if self.LMB is False:
2696 vBack = Pick(context, event, self)
2697 if vBack[0] is not None:
2698 NormalObject = Vector((0.0, 0.0, 1.0))
2699 self.aqR = RBenVe(NormalObject, vBack[1])
2700 self.qRot = vBack[3] * self.aqR
2701 self.am = event.mouse_region_x, event.mouse_region_y
2702 self.xSavMouse = event.mouse_region_x
2704 if self.ctrl:
2705 self.nRotZ = int((self.aRotZ / 25.0) / (PI / 4.0))
2706 self.aRotZ = self.nRotZ * (PI / 4.0) * 25.0
2708 self.LMB = True
2710 # LEFTMOUSE
2711 elif event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
2712 if self.ObjectMode or self.ProfileMode:
2713 # Rotation and scale
2714 self.LMB = False
2715 if self.ObjectScale is True:
2716 self.ObjectScale = False
2718 if self.GridScaleX is True:
2719 self.GridScaleX = False
2721 if self.GridScaleY is True:
2722 self.GridScaleY = False
2724 if self.WidthSolidify:
2725 self.WidthSolidify = False
2727 if self.CarveDepth is True:
2728 self.CarveDepth = False
2730 if self.BrushDepth is True:
2731 self.BrushDepth = False
2733 else:
2734 if self.bDone is False:
2735 if self.ctrl:
2736 Picking(context, event)
2737 else:
2738 if self.CutMode == LINE:
2739 if self.bDone is False:
2740 self.mouse_path.clear()
2741 self.mouse_path.append((event.mouse_region_x, event.mouse_region_y))
2742 self.mouse_path.append((event.mouse_region_x, event.mouse_region_y))
2743 else:
2744 self.mouse_path[0] = (event.mouse_region_x, event.mouse_region_y)
2745 self.mouse_path[1] = (event.mouse_region_x, event.mouse_region_y)
2746 self.bDone = True
2747 else:
2748 if self.CutMode != LINE:
2749 # Cut creation
2750 if self.CutMode == RECTANGLE:
2751 CreateCutSquare(self, context)
2752 if self.CutMode == CIRCLE:
2753 CreateCutCircle(self, context)
2754 if self.CutMode == LINE:
2755 CreateCutLine(self, context)
2757 if self.CreateMode:
2758 # Object creation
2759 self.CreateGeometry()
2760 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
2761 # Depth Cursor
2762 context.scene.mesh_carver.DepthCursor = self.snapCursor
2763 # Instantiate object
2764 context.scene.mesh_carver.OInstanciate = self.Instantiate
2765 # Random rotation
2766 context.scene.mesh_carver.ORandom = self.RandomRotation
2767 # Apply operation
2768 context.scene.mesh_carver.DontApply = self.DontApply
2770 # if Object mode, set intiale state
2771 if self.ObjectBrush is not None:
2772 self.ObjectBrush.location = self.InitBrushPosition
2773 self.ObjectBrush.scale = self.InitBrushScale
2774 self.ObjectBrush.rotation_quaternion = self.InitBrushQRotation
2775 self.ObjectBrush.rotation_euler = self.InitBrushERotation
2776 self.ObjectBrush.display_type = self.ObjectBrush_DT
2777 self.ObjectBrush.show_in_front = self.XRay
2779 # remove solidify
2780 Selection_Save(self)
2781 self.BrushSolidify = False
2783 bpy.ops.object.select_all(action='TOGGLE')
2784 self.ObjectBrush.select_set(True)
2785 context.view_layer.objects.active = self.ObjectBrush
2787 bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY")
2789 Selection_Restore(self)
2791 context.scene.mesh_carver.nProfile = self.nProfil
2793 return {'FINISHED'}
2794 else:
2795 self.Cut()
2796 UndoListUpdate(self)
2797 else:
2798 # Line
2799 self.mouse_path.append((event.mouse_region_x, event.mouse_region_y))
2801 # Change circle subdivisions
2802 elif (event.type == 'COMMA' and event.value == 'PRESS') or \
2803 (event.type == context.scene.Key_Subrem and event.value == 'PRESS'):
2804 if self.ProfileMode:
2805 self.nProfil += 1
2806 if self.nProfil >= self.MaxProfil:
2807 self.nProfil = 0
2808 createMeshFromData(self)
2809 # Circle rotation
2810 if self.CutMode == CIRCLE:
2811 if self.ctrl:
2812 self.stepRotation += 1
2813 else:
2814 self.step += 1
2815 if self.step >= len(self.stepAngle):
2816 self.step = len(self.stepAngle) - 1
2817 elif (event.type == 'PERIOD' and event.value == 'PRESS') or \
2818 (event.type == context.scene.Key_Subadd and event.value == 'PRESS'):
2819 if self.ProfileMode:
2820 self.nProfil -= 1
2821 if self.nProfil < 0:
2822 self.nProfil = self.MaxProfil - 1
2823 createMeshFromData(self)
2824 if self.CutMode == CIRCLE:
2825 if self.ctrl:
2826 self.stepRotation -= 1
2827 else:
2828 if self.step > 0:
2829 self.step -= 1
2830 # Quit
2831 elif event.type in {'RIGHTMOUSE', 'ESC'}:
2832 # Depth Cursor
2833 context.scene.mesh_carver.DepthCursor = self.snapCursor
2834 # Instantiate object
2835 context.scene.mesh_carver.OInstanciate = self.Instantiate
2836 # Random Rotation
2837 context.scene.mesh_carver.ORandom = self.RandomRotation
2838 # Apply boolean operation
2839 context.scene.mesh_carver.DontApply = self.DontApply
2841 # Reset Object
2842 if self.ObjectBrush is not None:
2843 self.ObjectBrush.location = self.InitBrushPosition
2844 self.ObjectBrush.scale = self.InitBrushScale
2845 self.ObjectBrush.rotation_quaternion = self.InitBrushQRotation
2846 self.ObjectBrush.rotation_euler = self.InitBrushERotation
2847 self.ObjectBrush.display_type = self.ObjectBrush_DT
2848 self.ObjectBrush.show_in_front = self.XRay
2850 # Remove solidify modifier
2851 Selection_Save(self)
2852 self.BrushSolidify = False
2854 bpy.ops.object.select_all(action='TOGGLE')
2855 self.ObjectBrush.select_set(True)
2856 context.view_layer.objects.active = self.ObjectBrush
2858 bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY")
2859 bpy.ops.object.select_all(action='TOGGLE')
2861 Selection_Restore(self)
2863 Selection_Save_Restore(self)
2864 context.view_layer.objects.active = self.CurrentActive
2865 context.scene.mesh_carver.nProfile = self.nProfil
2867 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
2869 # Remove Copy Object Brush
2870 if bpy.data.objects.get("CarverBrushCopy") is not None:
2871 brush = bpy.data.objects["CarverBrushCopy"]
2872 self.ObjectBrush.data = bpy.data.meshes[brush.data.name]
2873 bpy.ops.object.select_all(action='DESELECT')
2874 bpy.data.objects["CarverBrushCopy"].select_set(True)
2875 bpy.ops.object.delete()
2877 return {'FINISHED'}
2879 return {'RUNNING_MODAL'}
2881 except:
2882 print("\n[Carver MT ERROR]\n")
2883 import traceback
2884 traceback.print_exc()
2886 context.window.cursor_modal_set("DEFAULT")
2887 context.area.header_text_set(None)
2888 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
2890 self.report({'WARNING'},
2891 "Operation finished. Failure during Carving (Check the console for more info)")
2893 return {'FINISHED'}
2895 def cancel(self, context):
2896 # Note: used to prevent memory leaks on quitting Blender while the modal operator
2897 # is still running, gets called on return {"CANCELLED"}
2898 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
2900 def invoke(self, context, event):
2901 if context.area.type != 'VIEW_3D':
2902 self.report({'WARNING'},
2903 "View3D not found or not currently active. Operation Cancelled")
2904 return {'CANCELLED'}
2906 if context.mode == 'EDIT_MESH':
2907 bpy.ops.object.mode_set(mode='OBJECT')
2909 # test if some other object types are selected that are not meshes
2910 test_selection = True
2911 for obj in context.selected_objects:
2912 if obj.type != "MESH":
2913 test_selection = False
2914 break
2915 if not test_selection:
2916 self.report({'WARNING'},
2917 "Some selected objects are not of the Mesh type. Operation Cancelled")
2918 return {"CANCELLED"}
2920 # Get default patterns
2921 self.Profils = []
2922 for p in Profils:
2923 self.Profils.append((p[0], p[1], p[2], p[3]))
2925 for o in context.scene.objects:
2926 if not o.name.startswith(context.scene.ProfilePrefix):
2927 continue
2929 # In-scene profiles may have changed, remove them to refresh
2930 for m in bpy.data.meshes:
2931 if m.name.startswith(context.scene.ProfilePrefix):
2932 bpy.data.meshes.remove(m)
2934 vertices = []
2935 for v in o.data.vertices:
2936 vertices.append((v.co.x, v.co.y, v.co.z))
2938 faces = []
2939 for f in o.data.polygons:
2940 face = []
2941 for v in f.vertices:
2942 face.append(v)
2944 faces.append(face)
2946 self.Profils.append(
2947 (o.name,
2948 Vector((o.location.x, o.location.y, o.location.z)),
2949 vertices, faces)
2952 self.nProfil = context.scene.mesh_carver.nProfile
2953 self.MaxProfil = len(self.Profils)
2955 # reset selected profile if last profile exceeds length of array
2956 if self.nProfil >= self.MaxProfil:
2957 self.nProfil = context.scene.mesh_carver.nProfile = 0
2959 # Save selection
2960 self.CurrentSelection = context.selected_objects.copy()
2961 self.CurrentActive = context.active_object
2962 self.SavSel = context.selected_objects.copy()
2963 self.Sav_ac = None
2965 args = (self, context)
2967 self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
2969 self.mouse_path = [(0, 0), (0, 0)]
2971 self.shift = False
2972 self.ctrl = False
2973 self.alt = False
2974 self.bDone = False
2976 self.DontApply = context.scene.mesh_carver.DontApply
2977 self.Auto_BevelUpdate = True
2979 # Cut type (Rectangle, Circle, Line)
2980 self.CutMode = 0
2981 self.BoolOps = DIFFERENCE
2983 # Circle variables
2984 self.stepAngle = [2, 4, 5, 6, 9, 10, 15, 20, 30, 40, 45, 60, 72, 90]
2985 self.step = 4
2986 self.stepRotation = 0
2988 # Primitives Position
2989 self.xpos = 0
2990 self.ypos = 0
2991 self.InitPosition = False
2993 # Line Increment
2994 self.Increment = 15
2995 # Close polygonal shape
2996 self.Closed = False
2998 # Depth Cursor
2999 self.snapCursor = context.scene.mesh_carver.DepthCursor
3001 # Help
3002 self.AskHelp = False
3004 # Working object
3005 self.OpsObj = context.active_object
3007 # Create mode
3008 self.CreateMode = False
3009 self.ExclusiveCreateMode = False
3010 if len(context.selected_objects) == 0:
3011 self.ExclusiveCreateMode = True
3012 self.CreateMode = True
3014 # Rebool forced (cut line)
3015 self.ForceRebool = False
3017 self.ViewVector = Vector()
3018 self.CurrentObj = None
3020 # Brush
3021 self.BrushSolidify = False
3022 self.WidthSolidify = False
3023 self.CarveDepth = False
3024 self.BrushDepth = False
3025 self.BrushDepthOffset = 0.0
3027 self.ObjectScale = False
3029 self.CircleListRaw = []
3030 self.CLR_C = []
3031 self.CurLoc = Vector((0.0, 0.0, 0.0))
3032 self.SavCurLoc = Vector((0.0, 0.0, 0.0))
3033 self.CRadius = 1.0
3034 CreatePrimitive(self, 10.0, 1.0)
3035 self.VertsList = []
3036 self.FacesList = []
3038 self.am = -1, -1
3039 self.SavMousePos = None
3040 self.xSavMouse = 0
3042 self.ascale = 0
3043 self.aRotZ = 0
3044 self.nRotZ = 0
3045 self.aqR = None
3046 self.qRot = None
3048 self.RandomRotation = context.scene.mesh_carver.ORandom
3050 self.ShowCursor = True
3051 self.ObjectMode = False
3052 self.ProfileMode = False
3053 self.Instantiate = context.scene.mesh_carver.OInstanciate
3055 self.ProfileBrush = None
3056 self.ObjectBrush = None
3057 self.InitBrushPosition = None
3058 self.InitBrushScale = None
3059 self.InitBrushQRotation = None
3060 self.InitBrushERotation = None
3061 self.InitBrushARotation = None
3063 self.ObjectBrush_DT = "WIRE"
3064 self.XRay = False
3066 # Grid mesh
3067 self.nbcol = 1
3068 self.nbrow = 1
3069 self.gapx = 0
3070 self.gapy = 0
3071 self.scale_x = 1
3072 self.scale_y = 1
3074 self.GridScaleX = False
3075 self.GridScaleY = False
3077 if len(context.selected_objects) > 1:
3078 self.ObjectBrush = context.active_object
3080 # Copy the brush object
3081 ob = bpy.data.objects.new("CarverBrushCopy", context.object.data.copy())
3082 ob.location = self.ObjectBrush.location
3083 context.collection.objects.link(ob)
3084 context.scene.update()
3086 # Get default variables
3087 self.InitBrushPosition = self.ObjectBrush.location.copy()
3088 self.InitBrushScale = self.ObjectBrush.scale.copy()
3089 self.InitBrushQRotation = self.ObjectBrush.rotation_quaternion.copy()
3090 self.InitBrushERotation = self.ObjectBrush.rotation_euler.copy()
3091 self.ObjectBrush_DT = self.ObjectBrush.display_type
3092 self.XRay = self.ObjectBrush.show_in_front
3093 # Test if flat object
3094 z = self.ObjectBrush.data.vertices[0].co.z
3095 ErrorMarge = 0.01
3096 self.Solidify_Active_Start = True
3097 for v in self.ObjectBrush.data.vertices:
3098 if abs(v.co.z - z) > ErrorMarge:
3099 self.Solidify_Active_Start = False
3100 break
3101 self.SolidifyPossible = False
3103 self.CList = []
3104 self.OPList = []
3105 self.RList = []
3106 self.OB_List = []
3108 for ent in context.selected_objects:
3109 if ent != self.ObjectBrush:
3110 self.OB_List.append(ent)
3112 # Left button
3113 self.LMB = False
3115 # Undo Variables
3116 self.undo_index = 0
3117 self.undo_limit = context.preferences.edit.undo_steps
3118 self.undo_list = []
3120 # Boolean operations type
3121 self.BooleanType = 0
3123 self.UList = []
3124 self.UList_Index = -1
3125 self.UndoOps = []
3127 context.window_manager.modal_handler_add(self)
3128 return {'RUNNING_MODAL'}
3130 def CreateGeometry(self):
3131 context = bpy.context
3132 bLocalView = False
3134 for area in context.screen.areas:
3135 if area.type == 'VIEW_3D':
3136 if area.spaces[0].local_view is not None:
3137 bLocalView = True
3139 if bLocalView:
3140 bpy.ops.view3d.localview()
3142 if self.ExclusiveCreateMode:
3143 # Default width
3144 objBBDiagonal = 0.5
3145 else:
3146 ActiveObj = self.CurrentSelection[0]
3147 if ActiveObj is not None:
3148 objBBDiagonal = objDiagonal(ActiveObj) / 4
3149 subdivisions = 2
3151 if len(context.selected_objects) > 0:
3152 bpy.ops.object.select_all(action='TOGGLE')
3154 context.view_layer.objects.active = self.CurrentObj
3156 bpy.data.objects[self.CurrentObj.name].select_set(True)
3157 bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
3159 bpy.ops.object.mode_set(mode='EDIT')
3160 bpy.ops.mesh.select_all(action='SELECT')
3161 bpy.ops.mesh.select_mode(type="EDGE")
3162 if self.snapCursor is False:
3163 bpy.ops.transform.translate(value=self.ViewVector * objBBDiagonal * subdivisions)
3164 bpy.ops.mesh.extrude_region_move(
3165 TRANSFORM_OT_translate={"value": -self.ViewVector * objBBDiagonal * subdivisions * 2})
3167 bpy.ops.mesh.select_all(action='SELECT')
3168 bpy.ops.mesh.normals_make_consistent()
3169 bpy.ops.object.mode_set(mode='OBJECT')
3171 saved_location_0 = context.scene.cursor.location.copy()
3172 bpy.ops.view3d.snap_cursor_to_active()
3173 saved_location = context.scene.cursor.location.copy()
3174 bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
3175 context.scene.cursor.location = saved_location
3176 bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
3177 context.scene.cursor.location = saved_location_0
3179 bpy.data.objects[self.CurrentObj.name].select_set(True)
3180 bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
3182 for o in self.SavSel:
3183 bpy.data.objects[o.name].select_set(True)
3185 if bLocalView:
3186 bpy.ops.view3d.localview()
3188 self.bDone = False
3189 self.mouse_path.clear()
3190 self.mouse_path = [(0, 0), (0, 0)]
3192 def Cut(self):
3193 context = bpy.context
3195 # Local view ?
3196 bLocalView = False
3197 for area in context.screen.areas:
3198 if area.type == 'VIEW_3D':
3199 if area.spaces[0].local_view is not None:
3200 bLocalView = True
3202 if bLocalView:
3203 bpy.ops.view3d.localview()
3205 # Save cursor position
3206 CursorLocation = context.scene.cursor.location.copy()
3208 ActiveObjList = []
3209 if (self.ObjectMode is False) and (self.ProfileMode is False):
3210 objBBDiagonal = objDiagonal(self.CurrentSelection[0])
3211 subdivisions = 32
3212 if self.DontApply:
3213 subdivisions = 1
3215 # Get selected objects
3216 ActiveObjList = context.selected_objects.copy()
3218 bpy.ops.object.select_all(action='TOGGLE')
3220 context.view_layer.objects.active = self.CurrentObj
3222 bpy.data.objects[self.CurrentObj.name].select_set(True)
3223 bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
3225 bpy.ops.object.mode_set(mode='EDIT')
3226 bpy.ops.mesh.select_all(action='SELECT')
3227 bpy.ops.mesh.select_mode(type="EDGE")
3228 if (self.snapCursor is False) or (self.ForceRebool):
3229 bpy.ops.transform.translate(value=self.ViewVector * objBBDiagonal * subdivisions)
3230 bpy.ops.mesh.extrude_region_move(
3231 TRANSFORM_OT_translate={"value": -self.ViewVector * objBBDiagonal * subdivisions * 2})
3232 bpy.ops.mesh.select_all(action='SELECT')
3233 bpy.ops.mesh.normals_make_consistent()
3234 bpy.ops.object.mode_set(mode='OBJECT')
3235 else:
3236 # Create list
3237 if self.ObjectMode:
3238 for o in self.CurrentSelection:
3239 if o != self.ObjectBrush:
3240 ActiveObjList.append(o)
3241 self.CurrentObj = self.ObjectBrush
3242 else:
3243 ActiveObjList = self.CurrentSelection
3244 self.CurrentObj = self.ProfileBrush
3246 for o in self.CurrentSelection:
3247 UndoAdd(self, "MESH", o)
3249 # List objects create with rebool
3250 lastSelected = []
3252 for ActiveObj in ActiveObjList:
3253 context.scene.cursor.location = CursorLocation
3255 if len(context.selected_objects) > 0:
3256 bpy.ops.object.select_all(action='TOGGLE')
3258 # Test if initial object has bevel
3259 BevelAO = False
3260 for obj in ActiveObjList:
3261 for mb in obj.modifiers:
3262 if mb.type == 'BEVEL':
3263 BevelAO = True
3265 # Select cut object
3266 bpy.data.objects[self.CurrentObj.name].select_set(True)
3267 context.view_layer.objects.active = self.CurrentObj
3269 bpy.ops.object.mode_set(mode='EDIT')
3270 bpy.ops.mesh.select_all(action='SELECT')
3271 bpy.ops.object.mode_set(mode='OBJECT')
3273 # Select object to cut
3274 bpy.data.objects[ActiveObj.name].select_set(True)
3275 context.view_layer.objects.active = ActiveObj
3277 bpy.ops.object.mode_set(mode='EDIT')
3278 bpy.ops.mesh.select_all(action='DESELECT')
3279 bpy.ops.object.mode_set(mode='OBJECT')
3281 # Boolean operation
3282 if (self.shift is False) and (self.ForceRebool is False):
3283 if self.ObjectMode or self.ProfileMode:
3284 if self.BoolOps == UNION:
3285 boolean_operation(bool_type="UNION")
3286 else:
3287 boolean_operation(bool_type="DIFFERENCE")
3288 else:
3289 boolean_operation(bool_type="DIFFERENCE")
3291 # Apply booleans
3292 if self.DontApply is False:
3293 BMname = "CT_" + self.CurrentObj.name
3294 for mb in ActiveObj.modifiers:
3295 if (mb.type == 'BOOLEAN') and (mb.name == BMname):
3296 try:
3297 bpy.ops.object.modifier_apply(apply_as='DATA', modifier=BMname)
3298 except:
3299 bpy.ops.object.modifier_remove(modifier=BMname)
3300 exc_type, exc_value, exc_traceback = sys.exc_info()
3301 self.report({'ERROR'}, str(exc_value))
3303 bpy.ops.object.select_all(action='TOGGLE')
3304 else:
3305 if self.ObjectMode or self.ProfileMode:
3306 for mb in self.CurrentObj.modifiers:
3307 if (mb.type == 'SOLIDIFY') and (mb.name == "CT_SOLIDIFY"):
3308 try:
3309 bpy.ops.object.modifier_apply(apply_as='DATA', modifier="CT_SOLIDIFY")
3310 except:
3311 exc_type, exc_value, exc_traceback = sys.exc_info()
3312 self.report({'ERROR'}, str(exc_value))
3314 # Rebool
3315 Rebool(context, self)
3316 # Test if not empty object
3317 if context.selected_objects[0]:
3318 rebool_RT = context.selected_objects[0]
3319 if len(rebool_RT.data.vertices) > 0:
3320 # Create Bevel for new objects
3321 if BevelAO:
3322 CreateBevel(context, context.selected_objects[0])
3323 UndoAdd(self, "REBOOL", context.selected_objects[0])
3325 context.scene.cursor.location = ActiveObj.location
3326 bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
3327 else:
3328 bpy.ops.object.delete(use_global=False)
3330 context.scene.cursor.location = CursorLocation
3332 if self.ObjectMode:
3333 context.view_layer.objects.active = self.ObjectBrush
3334 if self.ProfileMode:
3335 context.view_layer.objects.active = self.ProfileBrush
3337 if self.DontApply is False:
3338 # Apply booleans
3339 BMname = "CT_" + self.CurrentObj.name
3340 for mb in ActiveObj.modifiers:
3341 if (mb.type == 'BOOLEAN') and (mb.name == BMname):
3342 try:
3343 bpy.ops.object.modifier_apply(apply_as='DATA', modifier=BMname)
3344 except:
3345 bpy.ops.object.modifier_remove(modifier=BMname)
3346 exc_type, exc_value, exc_traceback = sys.exc_info()
3347 self.report({'ERROR'}, str(exc_value))
3348 # Get new objects created with rebool operations
3349 if len(context.selected_objects) > 0:
3350 if self.shift is True:
3351 # Get the last object selected
3352 lastSelected.append(context.selected_objects[0])
3354 context.scene.cursor.location = CursorLocation
3356 if self.DontApply is False:
3357 # Remove cut object
3358 if (self.ObjectMode is False) and (self.ProfileMode is False):
3359 if len(context.selected_objects) > 0:
3360 bpy.ops.object.select_all(action='TOGGLE')
3361 bpy.data.objects[self.CurrentObj.name].select_set(True)
3362 bpy.ops.object.delete(use_global=False)
3363 else:
3364 if self.ObjectMode:
3365 self.ObjectBrush.display_type = self.ObjectBrush_DT
3367 if len(context.selected_objects) > 0:
3368 bpy.ops.object.select_all(action='TOGGLE')
3370 # Select cut objects
3371 for obj in lastSelected:
3372 bpy.data.objects[obj.name].select_set(True)
3374 for ActiveObj in ActiveObjList:
3375 bpy.data.objects[ActiveObj.name].select_set(True)
3376 context.view_layer.objects.active = ActiveObj
3377 # Update bevel
3378 list_act_obj = context.selected_objects.copy()
3379 if self.Auto_BevelUpdate:
3380 update_bevel(context)
3382 # Re-select initial objects
3383 bpy.ops.object.select_all(action='TOGGLE')
3384 if self.ObjectMode:
3385 # Re-select brush
3386 self.ObjectBrush.select_set(True)
3387 for ActiveObj in ActiveObjList:
3388 bpy.data.objects[ActiveObj.name].select_set(True)
3389 context.view_layer.objects.active = ActiveObj
3391 # If object has children, set "Wire" draw type
3392 if self.ObjectBrush is not None:
3393 if len(self.ObjectBrush.children) > 0:
3394 self.ObjectBrush.display_type = "WIRE"
3395 if self.ProfileMode:
3396 self.ProfileBrush.display_type = "WIRE"
3398 if bLocalView:
3399 bpy.ops.view3d.localview()
3401 # Reset variables
3402 self.bDone = False
3403 self.mouse_path.clear()
3404 self.mouse_path = [(0, 0), (0, 0)]
3406 self.ForceRebool = False
3409 class CarverProperties(bpy.types.PropertyGroup):
3410 DepthCursor: BoolProperty(
3411 name="DepthCursor",
3412 default=False
3414 OInstanciate: BoolProperty(
3415 name="Obj_Instantiate",
3416 default=False
3418 ORandom: BoolProperty(
3419 name="Random_Rotation",
3420 default=False
3422 DontApply: BoolProperty(
3423 name="Dont_Apply",
3424 default=False
3426 nProfile: IntProperty(
3427 name="Num_Profile",
3428 default=0
3432 addon_keymaps = []
3434 classes = (
3435 CarverPrefs,
3436 CarverProperties,
3437 Carver,
3441 def register():
3442 for cls in classes:
3443 bpy.utils.register_class(cls)
3445 bpy.types.Scene.mesh_carver = PointerProperty(
3446 type=CarverProperties
3448 # add keymap entry
3449 kcfg = bpy.context.window_manager.keyconfigs.addon
3450 if kcfg:
3451 km = kcfg.keymaps.new(name='3D View', space_type='VIEW_3D')
3452 kmi = km.keymap_items.new("object.carver", 'X', 'PRESS', shift=True, ctrl=True)
3453 addon_keymaps.append((km, kmi))
3456 def unregister():
3457 for cls in classes:
3458 bpy.utils.unregister_class(cls)
3460 # remove keymap entry
3461 for km, kmi in addon_keymaps:
3462 km.keymap_items.remove(kmi)
3463 addon_keymaps.clear()
3465 del bpy.types.Scene.mesh_carver
3468 if __name__ == "__main__":
3469 register()