File headers: use SPDX license identifiers
[blender-addons.git] / measureit / measureit_main.py
blobb93ff1ab032ca7515df5f4d7c81147d5bcbfe916
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # <pep8 compliant>
5 # ----------------------------------------------------------
6 # File: measureit_main.py
7 # Main panel for different Measureit general actions
8 # Author: Antonio Vazquez (antonioya)
10 # ----------------------------------------------------------
11 # noinspection PyUnresolvedReferences
12 import bpy
13 import bmesh
14 from bmesh import from_edit_mesh
15 # noinspection PyUnresolvedReferences
16 import bgl
17 from bpy.types import PropertyGroup, Panel, Object, Operator, SpaceView3D
18 from bpy.props import IntProperty, CollectionProperty, FloatVectorProperty, BoolProperty, StringProperty, \
19 FloatProperty, EnumProperty
20 from bpy.app.handlers import persistent
21 # noinspection PyUnresolvedReferences
22 from .measureit_geometry import *
23 from .measureit_render import *
26 # ------------------------------------------------------
27 # Handler to detect new Blend load
29 # ------------------------------------------------------
30 # noinspection PyUnusedLocal
31 @persistent
32 def load_handler(dummy):
33 MEASUREIT_OT_RunHintDisplay.handle_remove(None, bpy.context)
36 # ------------------------------------------------------
37 # Handler to detect save Blend
38 # Clear not used measured
40 # ------------------------------------------------------
41 # noinspection PyUnusedLocal
42 @persistent
43 def save_handler(dummy):
44 # noinspection PyBroadException
45 try:
46 print("MeasureIt: Cleaning data")
47 objlist = bpy.context.scene.objects
48 for myobj in objlist:
49 if 'MeasureGenerator' in myobj:
50 mp = myobj.MeasureGenerator[0]
51 x = 0
52 for ms in mp.measureit_segments:
53 ms.name = "segment_" + str(x)
54 x += 1
55 if ms.glfree is True:
56 idx = mp.measureit_segments.find(ms.name)
57 if idx > -1:
58 print("MeasureIt: Removed segment not used")
59 mp.measureit_segments.remove(idx)
61 # reset size
62 mp.measureit_num = len(mp.measureit_segments)
63 except:
64 pass
67 bpy.app.handlers.load_post.append(load_handler)
68 bpy.app.handlers.save_pre.append(save_handler)
71 # ------------------------------------------------------------------
72 # Define property group class for measureit faces index
73 # ------------------------------------------------------------------
74 class MeasureitIndex(PropertyGroup):
75 glidx: IntProperty(name="index", description="vertex index")
78 # Register
79 bpy.utils.register_class(MeasureitIndex)
82 # ------------------------------------------------------------------
83 # Define property group class for measureit faces
84 # ------------------------------------------------------------------
85 class MeasureitFaces(PropertyGroup):
86 glface: IntProperty(name="glface", description="Face number")
87 # Array of index
88 measureit_index: CollectionProperty(type=MeasureitIndex)
91 # Register
92 bpy.utils.register_class(MeasureitFaces)
95 # ------------------------------------------------------------------
96 # Define property group class for measureit data
97 # ------------------------------------------------------------------
98 class MeasureitProperties(PropertyGroup):
99 gltype: IntProperty(name="gltype",
100 description="Measure type (1-Segment, 2-Label, etc..)", default=1)
101 glpointa: IntProperty(name="glpointa",
102 description="Hidden property for opengl")
103 glpointb: IntProperty(name="glpointb",
104 description="Hidden property for opengl")
105 glpointc: IntProperty(name="glpointc",
106 description="Hidden property for opengl")
107 glcolor: FloatVectorProperty(name="glcolor",
108 description="Color for the measure",
109 default=(0.173, 0.545, 1.0, 1.0),
110 min=0.1,
111 max=1,
112 subtype='COLOR',
113 size=4)
114 glview: BoolProperty(name="glview",
115 description="Measure visible/hide",
116 default=True)
117 glspace: FloatProperty(name='glspace', min=-100, max=100, default=0.1,
118 precision=3,
119 description='Distance to display measure')
120 glwidth: IntProperty(name='glwidth', min=1, max=10, default=1,
121 description='line width')
122 glfree: BoolProperty(name="glfree",
123 description="This measure is free and can be deleted",
124 default=False)
125 gltxt: StringProperty(name="gltxt", maxlen=256,
126 description="Short description (use | for line break)")
127 gladvance: BoolProperty(name="gladvance",
128 description="Advanced options as line width or position",
129 default=False)
130 gldefault: BoolProperty(name="gldefault",
131 description="Display measure in position calculated by default",
132 default=True)
133 glnormalx: FloatProperty(name="glnormalx",
134 description="Change orientation in X axis",
135 default=1, min=-1, max=1, precision=2)
136 glnormaly: FloatProperty(name="glnormaly",
137 description="Change orientation in Y axis",
138 default=0, min=-1, max=1, precision=2)
139 glnormalz: FloatProperty(name="glnormalz",
140 description="Change orientation in Z axis",
141 default=0, min=-1, max=1, precision=2)
142 glfont_size: IntProperty(name="Text Size",
143 description="Text size",
144 default=14, min=6, max=150)
145 glfont_align: EnumProperty(items=(('L', "Left Align", ""),
146 ('C', "Center Align", ""),
147 ('R', "Right Align", "")),
148 name="Align Font",
149 description="Set Font Alignment")
150 glfont_rotat: IntProperty(name='Rotate', min=0, max=360, default=0,
151 description="Text rotation in degrees")
152 gllink: StringProperty(name="gllink",
153 description="linked object for linked measures")
154 glocwarning: BoolProperty(name="glocwarning",
155 description="Display a warning if some axis is not used in distance",
156 default=True)
157 glocx: BoolProperty(name="glocx",
158 description="Include changes in X axis for calculating the distance",
159 default=True)
160 glocy: BoolProperty(name="glocy",
161 description="Include changes in Y axis for calculating the distance",
162 default=True)
163 glocz: BoolProperty(name="glocz",
164 description="Include changes in Z axis for calculating the distance",
165 default=True)
166 glfontx: IntProperty(name="glfontx",
167 description="Change font position in X axis",
168 default=0, min=-3000, max=3000)
169 glfonty: IntProperty(name="glfonty",
170 description="Change font position in Y axis",
171 default=0, min=-3000, max=3000)
172 gldist: BoolProperty(name="gldist",
173 description="Display distance for this measure",
174 default=True)
175 glnames: BoolProperty(name="glnames",
176 description="Display text for this measure",
177 default=True)
178 gltot: EnumProperty(items=(('99', "-", "Select a group for sum"),
179 ('0', "A", ""),
180 ('1', "B", ""),
181 ('2', "C", ""),
182 ('3', "D", ""),
183 ('4', "E", ""),
184 ('5', "F", ""),
185 ('6', "G", ""),
186 ('7', "H", ""),
187 ('8', "I", ""),
188 ('9', "J", ""),
189 ('10', "K", ""),
190 ('11', "L", ""),
191 ('12', "M", ""),
192 ('13', "N", ""),
193 ('14', "O", ""),
194 ('15', "P", ""),
195 ('16', "Q", ""),
196 ('17', "R", ""),
197 ('18', "S", ""),
198 ('19', "T", ""),
199 ('20', "U", ""),
200 ('21', "V", ""),
201 ('22', "W", ""),
202 ('23', "X", ""),
203 ('24', "Y", ""),
204 ('25', "Z", "")),
205 name="Sum in Group",
206 description="Add segment length in selected group")
207 glorto: EnumProperty(items=(('99', "None", ""),
208 ('0', "A", "Point A must use selected point B location"),
209 ('1', "B", "Point B must use selected point A location")),
210 name="Orthogonal",
211 description="Display point selected as orthogonal (select axis to copy)")
212 glorto_x: BoolProperty(name="ox",
213 description="Copy X location",
214 default=False)
215 glorto_y: BoolProperty(name="oy",
216 description="Copy Y location",
217 default=False)
218 glorto_z: BoolProperty(name="oz",
219 description="Copy Z location",
220 default=False)
221 glarrow_a: EnumProperty(items=(('99', "--", "No arrow"),
222 ('1', "Line", "The point of the arrow are lines"),
223 ('2', "Triangle", "The point of the arrow is triangle"),
224 ('3', "TShape", "The point of the arrow is a T")),
225 name="A end",
226 description="Add arrows to point A")
227 glarrow_b: EnumProperty(items=(('99', "--", "No arrow"),
228 ('1', "Line", "The point of the arrow are lines"),
229 ('2', "Triangle", "The point of the arrow is triangle"),
230 ('3', "TShape", "The point of the arrow is a T")),
231 name="B end",
232 description="Add arrows to point B")
233 glarrow_s: IntProperty(name="Size",
234 description="Arrow size",
235 default=15, min=6, max=500)
237 glarc_full: BoolProperty(name="arcfull",
238 description="Create full circunference",
239 default=False)
240 glarc_extrad: BoolProperty(name="arcextrad",
241 description="Adapt radio length to arc line",
242 default=True)
243 glarc_rad: BoolProperty(name="arc rad",
244 description="Show arc radius",
245 default=True)
246 glarc_len: BoolProperty(name="arc len",
247 description="Show arc length",
248 default=True)
249 glarc_ang: BoolProperty(name="arc ang",
250 description="Show arc angle",
251 default=True)
253 glarc_a: EnumProperty(items=(('99', "--", "No arrow"),
254 ('1', "Line", "The point of the arrow are lines"),
255 ('2', "Triangle", "The point of the arrow is triangle"),
256 ('3', "TShape", "The point of the arrow is a T")),
257 name="Ar end",
258 description="Add arrows to point A")
259 glarc_b: EnumProperty(items=(('99', "--", "No arrow"),
260 ('1', "Line", "The point of the arrow are lines"),
261 ('2', "Triangle", "The point of the arrow is triangle"),
262 ('3', "TShape", "The point of the arrow is a T")),
263 name="Br end",
264 description="Add arrows to point B")
265 glarc_s: IntProperty(name="Size",
266 description="Arrow size",
267 default=15, min=6, max=500)
268 glarc_txradio: StringProperty(name="txradio",
269 description="Text for radius", default="r=")
270 glarc_txlen: StringProperty(name="txlen",
271 description="Text for length", default="L=")
272 glarc_txang: StringProperty(name="txang",
273 description="Text for angle", default="A=")
274 glcolorarea: FloatVectorProperty(name="glcolorarea",
275 description="Color for the measure of area",
276 default=(0.1, 0.1, 0.1, 1.0),
277 min=0.1,
278 max=1,
279 subtype='COLOR',
280 size=4)
282 # Array of faces
283 measureit_faces: CollectionProperty(type=MeasureitFaces)
286 # Register
287 bpy.utils.register_class(MeasureitProperties)
290 # ------------------------------------------------------------------
291 # Define object class (container of segments)
292 # Measureit
293 # ------------------------------------------------------------------
294 class MeasureContainer(PropertyGroup):
295 measureit_num: IntProperty(name='Number of measures', min=0, max=1000, default=0,
296 description='Number total of measureit elements')
297 # Array of segments
298 measureit_segments: CollectionProperty(type=MeasureitProperties)
301 bpy.utils.register_class(MeasureContainer)
302 Object.MeasureGenerator = CollectionProperty(type=MeasureContainer)
305 # ------------------------------------------------------------------
306 # Define UI class
307 # Measureit
308 # ------------------------------------------------------------------
309 class MEASUREIT_PT_Edit(Panel):
310 bl_idname = "MEASUREIT_PT_Edit"
311 bl_label = "Items"
312 bl_space_type = 'VIEW_3D'
313 bl_region_type = 'UI'
314 bl_category= 'View'
315 bl_parent_id = 'MEASUREIT_PT_Main'
317 # -----------------------------------------------------
318 # Verify if visible
319 # -----------------------------------------------------
320 @classmethod
321 def poll(cls, context):
322 o = context.object
323 if o is None:
324 return False
325 if 'MeasureGenerator' not in o:
326 return False
327 else:
328 mp = context.object.MeasureGenerator[0]
329 if mp.measureit_num > 0:
330 return True
331 else:
332 return False
334 # -----------------------------------------------------
335 # Draw (create UI interface)
336 # -----------------------------------------------------
337 # noinspection PyUnusedLocal
338 def draw(self, context):
339 layout = self.layout
340 scene = context.scene
341 if context.object is not None:
342 if 'MeasureGenerator' in context.object:
343 box = layout.box()
344 row = box.row()
345 row.label(text=context.object.name)
346 row = box.row()
347 row.prop(scene, 'measureit_gl_precision', text="Precision")
348 row.prop(scene, 'measureit_units')
349 row = box.row()
350 row.prop(scene, 'measureit_gl_show_d', text="Distances", toggle=True, icon="ALIGN_CENTER")
351 row.prop(scene, 'measureit_gl_show_n', text="Texts", toggle=True, icon="FONT_DATA")
352 row = box.row()
353 row.prop(scene, 'measureit_hide_units', text="Hide measurement unit")
354 # Scale factor
355 row = box.row()
356 row.prop(scene, 'measureit_scale', text="Scale")
357 if scene.measureit_scale is True:
358 split = row.split(factor=0.25, align=False)
359 split.prop(scene, 'measureit_scale_color', text="")
360 split.prop(scene, 'measureit_scale_factor', text="1")
361 row = box.row()
362 row.separator()
363 row.prop(scene, 'measureit_gl_scaletxt', text="")
364 row.prop(scene, 'measureit_scale_font')
365 row.prop(scene, 'measureit_scale_precision', text="")
366 row = box.row()
367 row.separator()
368 row.prop(scene, 'measureit_scale_pos_x')
369 row.prop(scene, 'measureit_scale_pos_y')
371 # Override
372 row = box.row()
373 row.prop(scene, 'measureit_ovr', text="Override")
374 if scene.measureit_ovr is True:
375 split = row.split(factor=0.25, align=False)
376 split.prop(scene, 'measureit_ovr_color', text="")
377 split.prop(scene, 'measureit_ovr_width', text="Width")
378 row = box.row()
379 row.separator()
380 row.prop(scene, 'measureit_ovr_font', text="Font")
381 row.prop(scene, 'measureit_ovr_font_align', text="")
382 if scene.measureit_ovr_font_align == 'L':
383 row.prop(scene, 'measureit_ovr_font_rotation', text="Rotate")
385 mp = context.object.MeasureGenerator[0]
386 # -----------------
387 # loop
388 # -----------------
389 if mp.measureit_num > 0:
390 box = layout.box()
391 row = box.row(align=True)
392 row.operator("measureit.expandallsegment", text="Expand all", icon="ZOOM_IN")
393 row.operator("measureit.collapseallsegment", text="Collapse all", icon="ZOOM_OUT")
394 for idx in range(mp.measureit_num):
395 if mp.measureit_segments[idx].glfree is False:
396 add_item(box, idx, mp.measureit_segments[idx])
398 row = box.row()
399 row.operator("measureit.deleteallsegment", text="Delete all", icon="X")
400 # -----------------
401 # Sum loop segments
402 # -----------------
403 if mp.measureit_num > 0:
404 scale = bpy.context.scene.unit_settings.scale_length
405 tx = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S",
406 "T", "U", "V", "W", "X", "Y", "Z"]
407 tot = [0.0] * len(tx)
408 ac = [False] * len(tx)
409 myobj = context.object
410 obverts = get_mesh_vertices(myobj)
411 viewtot = False
412 for idx in range(mp.measureit_num):
413 ms = mp.measureit_segments[idx]
414 if (ms.gltype == 1 or ms.gltype == 12
415 or ms.gltype == 13 or ms.gltype == 14) and ms.gltot != '99' \
416 and ms.glfree is False: # only segments
417 if bpy.context.mode == "EDIT_MESH":
418 bm = bmesh.from_edit_mesh(bpy.context.edit_object.data)
419 if hasattr(bm.verts, "ensure_lookup_table"):
420 bm.verts.ensure_lookup_table()
421 if ms.glpointa <= len(obverts) and ms.glpointb <= len(obverts):
422 p1 = get_point(obverts[ms.glpointa].co, myobj)
423 if ms.gltype == 1:
424 p2 = get_point(obverts[ms.glpointb].co, myobj)
425 elif ms.gltype == 12:
426 p2 = get_point((0.0,
427 obverts[ms.glpointa].co[1],
428 obverts[ms.glpointa].co[2]), myobj)
429 elif ms.gltype == 13:
430 p2 = get_point((obverts[ms.glpointa].co[0],
431 0.0,
432 obverts[ms.glpointa].co[2]), myobj)
433 else:
434 p2 = get_point((obverts[ms.glpointa].co[0],
435 obverts[ms.glpointa].co[1],
436 0.0), myobj)
438 dist, distloc = distance(p1, p2, ms.glocx, ms.glocy, ms.glocz)
439 if dist == distloc:
440 usedist = dist
441 else:
442 usedist = distloc
443 usedist *= scale
444 tot[int(ms.gltot)] += usedist
445 ac[int(ms.gltot)] = True
446 viewtot = True
447 # -----------------
448 # Print values
449 # -----------------
450 if viewtot is True:
451 pr = scene.measureit_gl_precision
452 fmt = "%1." + str(pr) + "f"
453 units = scene.measureit_units
455 box = layout.box()
456 box.label(text="Totals", icon='SOLO_ON')
457 final = 0
458 for idx in range(len(tot)):
459 if ac[idx] is True:
460 final += tot[idx]
461 tx_dist = format_distance(fmt, units, tot[idx])
462 row = box.row(align=True)
463 row.label(text="Group " + tx[idx] + ":")
464 row.label(text=" ")
465 row.label(text=tx_dist)
467 # Grand total
468 row = box.row(align=True)
469 row.label(text="")
470 row.label(text=" ")
471 row.label(text="-" * 20)
472 tx_dist = format_distance(fmt, units, final)
474 row = box.row(align=True)
475 row.label(text="")
476 row.label(text=" ")
477 row.label(text=tx_dist)
478 # delete all
479 row = box.row()
480 row.operator("measureit.deleteallsum", text="Delete all", icon="X")
483 # -----------------------------------------------------
484 # Add segment options to the panel.
485 # -----------------------------------------------------
486 def add_item(box, idx, segment):
487 scene = bpy.context.scene
488 row = box.row(align=True)
489 if segment.glview is True:
490 icon = "HIDE_OFF"
491 else:
492 icon = "HIDE_ON"
494 row.prop(segment, 'glview', text="", toggle=True, icon=icon)
495 row.prop(segment, 'gladvance', text="", toggle=True, icon="PREFERENCES")
496 if segment.gltype == 20: # Area special
497 split = row.split(factor=0.15, align=True)
498 split.prop(segment, 'glcolorarea', text="")
499 split = split.split(factor=0.20, align=True)
500 split.prop(segment, 'glcolor', text="")
501 else:
502 split = row.split(factor=0.25, align=True)
503 split.prop(segment, 'glcolor', text="")
504 split.prop(segment, 'gltxt', text="")
505 op = row.operator("measureit.deletesegment", text="", icon="X")
506 op.tag = idx # saves internal data
507 if segment.gladvance is True:
508 row = box.row(align=True)
509 row.prop(segment, 'glfont_size', text="Font")
510 row.prop(segment, 'glfont_align', text="")
511 if segment.glfont_align == 'L':
512 row.prop(segment, 'glfont_rotat', text="Rotate")
513 row = box.row(align=True)
514 if segment.gltype != 9 and segment.gltype != 10 and segment.gltype != 20:
515 row.prop(segment, 'glspace', text="Distance")
516 row.prop(segment, 'glfontx', text="X")
517 row.prop(segment, 'glfonty', text="Y")
519 # Arrows
520 if segment.gltype != 9 and segment.gltype != 10 and segment.gltype != 20:
521 row = box.row(align=True)
522 row.prop(segment, 'glarrow_a', text="")
523 row.prop(segment, 'glarrow_b', text="")
524 if segment.glarrow_a != '99' or segment.glarrow_b != '99':
525 row.prop(segment, 'glarrow_s', text="Size")
527 if segment.gltype != 2 and segment.gltype != 10:
528 row = box.row(align=True)
529 if scene.measureit_gl_show_d is True and segment.gltype != 9:
530 row.prop(segment, 'gldist', text="Distance", toggle=True, icon="ALIGN_CENTER")
531 if scene.measureit_gl_show_n is True:
532 row.prop(segment, 'glnames', text="Text", toggle=True, icon="FONT_DATA")
533 # sum distances
534 if segment.gltype == 1 or segment.gltype == 12 or segment.gltype == 13 or segment.gltype == 14:
535 row.prop(segment, 'gltot', text="Sum")
537 if segment.gltype != 9 and segment.gltype != 10 and segment.gltype != 20:
538 row = box.row(align=True)
539 row.prop(segment, 'glwidth', text="Line")
540 row.prop(segment, 'gldefault', text="Automatic position")
541 if segment.gldefault is False:
542 row = box.row(align=True)
543 row.prop(segment, 'glnormalx', text="X")
544 row.prop(segment, 'glnormaly', text="Y")
545 row.prop(segment, 'glnormalz', text="Z")
547 # Loc axis
548 if segment.gltype != 2 and segment.gltype != 9 and segment.gltype != 10 \
549 and segment.gltype != 11 and segment.gltype != 12 and segment.gltype != 13 \
550 and segment.gltype != 14 and segment.gltype != 20:
551 row = box.row(align=True)
552 row.prop(segment, 'glocx', text="X", toggle=True)
553 row.prop(segment, 'glocy', text="Y", toggle=True)
554 row.prop(segment, 'glocz', text="Z", toggle=True)
555 if segment.glocx is False or segment.glocy is False or segment.glocz is False:
556 row = box.row()
557 if segment.gltype == 1:
558 row.prop(segment, 'glorto', text="Orthogonal")
559 row.prop(segment, 'glocwarning', text="Warning")
560 # ortogonal (only segments)
561 if segment.gltype == 1:
562 if segment.glorto != "99":
563 row = box.row(align=True)
564 row.prop(segment, 'glorto_x', text="X", toggle=True)
565 row.prop(segment, 'glorto_y', text="Y", toggle=True)
566 row.prop(segment, 'glorto_z', text="Z", toggle=True)
568 # Arc special
569 if segment.gltype == 11:
570 row = box.row(align=True)
571 row.prop(segment, 'glarc_rad', text="Radius")
572 row.prop(segment, 'glarc_len', text="Length")
573 row.prop(segment, 'glarc_ang', text="Angle")
575 row = box.row(align=True)
576 row.prop(segment, 'glarc_txradio', text="")
577 row.prop(segment, 'glarc_txlen', text="")
578 row.prop(segment, 'glarc_txang', text="")
579 row = box.row(align=True)
580 row.prop(segment, 'glarc_full', text="Full Circle")
581 if segment.glarc_rad is True:
582 row.prop(segment, 'glarc_extrad', text="Adapt radio")
584 row = box.row(align=True)
585 row.prop(segment, 'glarc_a', text="")
586 row.prop(segment, 'glarc_b', text="")
587 if segment.glarc_a != '99' or segment.glarc_b != '99':
588 row.prop(segment, 'glarc_s', text="Size")
591 # ------------------------------------------------------------------
592 # Define panel class for main functions.
593 # ------------------------------------------------------------------
594 class MEASUREIT_PT_Main(Panel):
595 bl_idname = "MEASUREIT_PT_Main"
596 bl_label = "MeasureIt Tools"
597 bl_space_type = 'VIEW_3D'
598 bl_region_type = 'UI'
599 bl_category= 'View'
600 bl_options = {'DEFAULT_CLOSED'}
602 # ------------------------------
603 # Draw UI
604 # ------------------------------
605 def draw(self, context):
606 layout = self.layout
607 scene = context.scene
609 # ------------------------------
610 # Tool Buttons
611 # ------------------------------
612 box = layout.box()
613 # ------------------------------
614 # Display Buttons
615 # ------------------------------
616 row = box.row()
617 if context.window_manager.measureit_run_opengl is False:
618 icon = 'PLAY'
619 txt = 'Show'
620 else:
621 icon = "PAUSE"
622 txt = 'Hide'
624 row.operator("measureit.runopengl", text=txt, icon=icon)
625 row.prop(scene, "measureit_gl_ghost", text="", icon='GHOST_ENABLED')
627 # Tools
628 box = layout.box()
629 box.label(text="Add Measures")
630 row = box.row()
631 row.operator("measureit.addsegment", text="Segment")
632 row.prop(scene, "measureit_sum", text="Sum")
634 # To origin
635 row = box.row()
636 op = row.operator("measureit.addsegmentorto", text="X")
637 op.tag = 0 # saves internal data
638 op = row.operator("measureit.addsegmentorto", text="Y")
639 op.tag = 1 # saves internal data
640 op = row.operator("measureit.addsegmentorto", text="Z")
641 op.tag = 2 # saves internal data
643 row = box.row()
644 row.operator("measureit.addangle", text="Angle", icon="LINCURVE")
645 row.operator("measureit.addarc", text="Arc")
647 row = box.row()
648 row.operator("measureit.addlabel", text="Label", icon="FONT_DATA")
649 row.operator("measureit.addnote", text="Annotation")
651 row = box.row()
652 row.operator("measureit.addlink", text="Link")
653 row.operator("measureit.addorigin", text="Origin")
655 row = box.row()
656 row.operator("measureit.addarea", text="Area", icon="MESH_GRID")
658 # ------------------------------
659 # Debug data
660 # ------------------------------
661 box = layout.box()
662 row = box.row(align=False)
663 if scene.measureit_debug is False:
664 row.prop(scene, "measureit_debug", icon="TRIA_RIGHT",
665 text="Mesh Debug", emboss=False)
666 else:
667 row.prop(scene, "measureit_debug", icon="TRIA_DOWN",
668 text="Mesh Debug", emboss=False)
670 row = box.row()
671 split = row.split(factor=0.10, align=True)
672 split.prop(scene, 'measureit_debug_obj_color', text="")
673 split.prop(scene, "measureit_debug_objects", icon="OBJECT_DATA")
674 split.prop(scene, "measureit_debug_object_loc", icon="EMPTY_DATA")
676 row = box.row()
677 split = row.split(factor=0.10, align=True)
678 split.prop(scene, 'measureit_debug_vert_color', text="")
679 split.prop(scene, "measureit_debug_vertices", icon="VERTEXSEL")
680 split.prop(scene, "measureit_debug_vert_loc", icon="EMPTY_DATA")
681 if scene.measureit_debug_vert_loc is True:
682 split.prop(scene, 'measureit_debug_vert_loc_toggle', text="")
684 row = box.row()
685 split = row.split(factor=0.10, align=True)
686 split.prop(scene, 'measureit_debug_edge_color', text="")
687 split = split.split(factor=0.5, align=True)
688 split.prop(scene, "measureit_debug_edges", icon="EDGESEL")
690 row = box.row()
691 split = row.split(factor=0.10, align=True)
692 split.prop(scene, 'measureit_debug_face_color', text="")
693 split = split.split(factor=0.5, align=True)
694 split.prop(scene, "measureit_debug_faces", icon="FACESEL")
696 row = box.row()
697 split = row.split(factor=0.10, align=True)
698 split.prop(scene, 'measureit_debug_norm_color', text="")
699 if scene.measureit_debug_normals is False:
700 split = split.split(factor=0.50, align=True)
701 split.prop(scene, "measureit_debug_normals", icon="OBJECT_ORIGIN")
702 else:
703 split = split.split(factor=0.5, align=True)
704 split.prop(scene, "measureit_debug_normals", icon="OBJECT_ORIGIN")
705 split.prop(scene, "measureit_debug_normal_size")
706 row = box.row()
707 split = row.split(factor=0.10, align=True)
708 split.separator()
709 split.prop(scene, "measureit_debug_normal_details")
710 split.prop(scene, 'measureit_debug_width', text="Thickness")
712 row = box.row(align=True)
713 row.prop(scene, "measureit_debug_select", icon="GHOST_ENABLED")
714 row.prop(scene, 'measureit_debug_font', text="Font")
715 row.prop(scene, 'measureit_debug_precision', text="Precision")
718 # ------------------------------------------------------------------
719 # Define panel class for conf functions.
720 # ------------------------------------------------------------------
721 class MEASUREIT_PT_Conf(Panel):
722 bl_idname = "MEASUREIT_PT_Conf"
723 bl_label = "Configuration"
724 bl_space_type = 'VIEW_3D'
725 bl_region_type = 'UI'
726 bl_category= 'View'
727 bl_parent_id = 'MEASUREIT_PT_Main'
728 bl_options = {'DEFAULT_CLOSED'}
730 # ------------------------------
731 # Draw UI
732 # ------------------------------
733 def draw(self, context):
734 layout = self.layout
735 scene = context.scene
737 # Configuration data
738 box = layout.box()
739 row = box.row()
740 split = row.split(factor=0.2, align=True)
741 split.label(text="Text")
742 split = split.split(factor=0.2, align=True)
743 split.prop(scene, "measureit_default_color", text="")
744 split.prop(scene, "measureit_gl_txt", text="")
745 row = box.row(align=True)
746 row.prop(scene, "measureit_hint_space")
747 row.prop(scene, "measureit_font_align", text="")
748 # Arrow
749 row = box.row(align=True)
750 row.prop(scene, "measureit_glarrow_a", text="")
751 row.prop(scene, "measureit_glarrow_b", text="")
752 if scene.measureit_glarrow_a != '99' or scene.measureit_glarrow_b != '99':
753 row.prop(scene, "measureit_glarrow_s", text="Size")
754 row = box.row(align=True)
755 row.prop(scene, "measureit_font_size")
756 if scene.measureit_font_align == 'L':
757 row.prop(scene, "measureit_font_rotation", text="Rotate")
760 # ------------------------------------------------------------------
761 # Define panel class for render functions.
762 # ------------------------------------------------------------------
763 class MEASUREIT_PT_Render(Panel):
764 bl_idname = "MEASUREIT_PT_Render"
765 bl_label = "Render"
766 bl_space_type = 'VIEW_3D'
767 bl_region_type = 'UI'
768 bl_category= 'Display'
769 bl_parent_id = 'MEASUREIT_PT_Main'
770 bl_options = {'DEFAULT_CLOSED'}
772 # ------------------------------
773 # Draw UI
774 # ------------------------------
775 def draw(self, context):
776 layout = self.layout
777 scene = context.scene
779 # Render settings
780 box = layout.box()
781 row = box.row()
782 row.prop(scene, "measureit_render_type")
783 row = box.row()
784 row.operator("measureit.rendersegment", icon='SCRIPT')
785 row = box.row()
786 row.prop(scene, "measureit_render", text="Save render image")
787 row = box.row()
788 row.prop(scene, "measureit_rf", text="Frame")
789 if scene.measureit_rf is True:
790 row.prop(scene, "measureit_rf_color", text="Color")
791 row = box.row()
792 row.prop(scene, "measureit_rf_border", text="Space")
793 row.prop(scene, "measureit_rf_line", text="Width")
796 # -------------------------------------------------------------
797 # Defines button that adds a measure segment
799 # -------------------------------------------------------------
800 class MEASUREIT_OT_AddSegment(Operator):
801 bl_idname = "measureit.addsegment"
802 bl_label = "Add"
803 bl_description = "(EDITMODE only) Add a new measure segment between 2 vertices (select 2 vertices or more)"
805 # ------------------------------
806 # Poll
807 # ------------------------------
808 @classmethod
809 def poll(cls, context):
810 o = context.object
811 if o is None:
812 return False
813 else:
814 if o.type == "MESH":
815 if bpy.context.mode == 'EDIT_MESH':
816 return True
817 else:
818 return False
819 else:
820 return False
822 # ------------------------------
823 # Execute button action
824 # ------------------------------
825 def execute(self, context):
826 if context.area.type == 'VIEW_3D':
827 # Add properties
828 scene = context.scene
829 mainobject = context.object
830 mylist = get_smart_selected(mainobject)
831 if len(mylist) < 2: # if not selected linked vertex
832 mylist = get_selected_vertex(mainobject)
834 if len(mylist) >= 2:
835 if 'MeasureGenerator' not in mainobject:
836 mainobject.MeasureGenerator.add()
838 mp = mainobject.MeasureGenerator[0]
839 for x in range(0, len(mylist) - 1, 2):
840 # -----------------------
841 # Only if not exist
842 # -----------------------
843 if exist_segment(mp, mylist[x], mylist[x + 1]) is False:
844 # Create all array elements
845 for cont in range(len(mp.measureit_segments) - 1, mp.measureit_num):
846 mp.measureit_segments.add()
848 # Set values
849 ms = mp.measureit_segments[mp.measureit_num]
850 ms.gltype = 1
851 ms.glpointa = mylist[x]
852 ms.glpointb = mylist[x + 1]
853 ms.glarrow_a = scene.measureit_glarrow_a
854 ms.glarrow_b = scene.measureit_glarrow_b
855 ms.glarrow_s = scene.measureit_glarrow_s
856 # color
857 ms.glcolor = scene.measureit_default_color
858 # dist
859 ms.glspace = scene.measureit_hint_space
860 # text
861 ms.gltxt = scene.measureit_gl_txt
862 ms.glfont_size = scene.measureit_font_size
863 ms.glfont_align = scene.measureit_font_align
864 ms.glfont_rotat = scene.measureit_font_rotation
865 # Sum group
866 ms.gltot = scene.measureit_sum
867 # Add index
868 mp.measureit_num += 1
870 # redraw
871 context.area.tag_redraw()
872 return {'FINISHED'}
873 else:
874 self.report({'ERROR'},
875 "MeasureIt: Select at least two vertices for creating measure segment.")
876 return {'FINISHED'}
877 else:
878 self.report({'WARNING'},
879 "View3D not found, cannot run operator")
881 return {'CANCELLED'}
884 # -------------------------------------------------------------
885 # Defines button that adds an area measure
887 # -------------------------------------------------------------
888 class MEASUREIT_OT_AddArea(Operator):
889 bl_idname = "measureit.addarea"
890 bl_label = "Area"
891 bl_description = "(EDITMODE only) Add a new measure for area (select 1 o more faces)"
893 # ------------------------------
894 # Poll
895 # ------------------------------
896 @classmethod
897 def poll(cls, context):
898 o = context.object
899 if o is None:
900 return False
901 else:
902 if o.type == "MESH":
903 if bpy.context.mode == 'EDIT_MESH':
904 return True
905 else:
906 return False
907 else:
908 return False
910 # ------------------------------
911 # Execute button action
912 # ------------------------------
913 def execute(self, context):
914 if context.area.type == 'VIEW_3D':
915 # Add properties
916 scene = context.scene
917 mainobject = context.object
918 mylist = get_selected_faces(mainobject)
919 if len(mylist) >= 1:
920 if 'MeasureGenerator' not in mainobject:
921 mainobject.MeasureGenerator.add()
923 mp = mainobject.MeasureGenerator[0]
924 mp.measureit_segments.add()
925 ms = mp.measureit_segments[mp.measureit_num]
926 ms.gltype = 20
928 f = -1
929 for face in mylist:
930 # Create array elements
931 ms.measureit_faces.add()
932 f += 1
933 # Set values
934 mf = ms.measureit_faces[f]
935 mf.glface = f
936 i = 0
937 for v in face:
938 mf.measureit_index.add()
939 mi = mf.measureit_index[i]
940 mi.glidx = v
941 i += 1
943 # color
944 rgb = scene.measureit_default_color
945 ms.glcolor = (rgb[0], rgb[1], rgb[2], 0.4)
946 # dist
947 ms.glspace = scene.measureit_hint_space
948 # text
949 ms.gltxt = scene.measureit_gl_txt
950 ms.glfont_size = scene.measureit_font_size
951 ms.glfont_align = scene.measureit_font_align
952 ms.glfont_rotat = scene.measureit_font_rotation
953 # Sum group
954 ms.gltot = scene.measureit_sum
955 # Add index
956 mp.measureit_num += 1
957 # redraw
958 context.area.tag_redraw()
959 return {'FINISHED'}
960 else:
961 self.report({'ERROR'},
962 "MeasureIt: Select at least one face for creating area measure. ")
963 return {'FINISHED'}
964 else:
965 self.report({'WARNING'},
966 "View3D not found, cannot run operator")
968 return {'CANCELLED'}
971 # -------------------------------------------------------------
972 # Defines button that adds a measure segment to x/y/z origin
974 # -------------------------------------------------------------
975 class MEASUREIT_OT_AddSegmentOrto(Operator):
976 bl_idname = "measureit.addsegmentorto"
977 bl_label = "Add"
978 bl_description = "(EDITMODE only) Add a new measure segment from vertex to object origin for one " \
979 "axis (select 1 vertex)"
980 tag: IntProperty()
982 # ------------------------------
983 # Poll
984 # ------------------------------
985 @classmethod
986 def poll(cls, context):
987 o = context.object
988 if o is None:
989 return False
990 else:
991 if o.type == "MESH":
992 if bpy.context.mode == 'EDIT_MESH':
993 return True
994 else:
995 return False
996 else:
997 return False
999 # ------------------------------
1000 # Execute button action
1001 # ------------------------------
1002 def execute(self, context):
1003 if context.area.type == 'VIEW_3D':
1004 # Add properties
1005 scene = context.scene
1006 mainobject = context.object
1007 mylist = get_smart_selected(mainobject)
1008 if len(mylist) < 1: # if not selected linked vertex
1009 mylist = get_selected_vertex(mainobject)
1011 if len(mylist) >= 1:
1012 if 'MeasureGenerator' not in mainobject:
1013 mainobject.MeasureGenerator.add()
1015 mp = mainobject.MeasureGenerator[0]
1016 for x in range(len(mylist)):
1017 # -----------------------
1018 # Only if not exist
1019 # -----------------------
1020 if exist_segment(mp, mylist[x], mylist[x], 12 + int(self.tag)) is False:
1021 # Create all array elements
1022 for cont in range(len(mp.measureit_segments) - 1, mp.measureit_num):
1023 mp.measureit_segments.add()
1025 # Set values
1026 ms = mp.measureit_segments[mp.measureit_num]
1027 ms.gltype = 12 + int(self.tag)
1028 ms.glpointa = mylist[x]
1029 ms.glpointb = mylist[x]
1030 ms.glarrow_a = scene.measureit_glarrow_a
1031 ms.glarrow_b = scene.measureit_glarrow_b
1032 ms.glarrow_s = scene.measureit_glarrow_s
1033 # color
1034 ms.glcolor = scene.measureit_default_color
1035 # dist
1036 ms.glspace = scene.measureit_hint_space
1037 # text
1038 ms.gltxt = scene.measureit_gl_txt
1039 ms.glfont_size = scene.measureit_font_size
1040 ms.glfont_align = scene.measureit_font_align
1041 ms.glfont_rotat = scene.measureit_font_rotation
1042 # Sum group
1043 ms.gltot = scene.measureit_sum
1044 # Add index
1045 mp.measureit_num += 1
1047 # redraw
1048 context.area.tag_redraw()
1049 return {'FINISHED'}
1050 else:
1051 self.report({'ERROR'},
1052 "MeasureIt: Select at least one vertex for creating measure segment.")
1053 return {'FINISHED'}
1054 else:
1055 self.report({'WARNING'},
1056 "View3D not found, cannot run operator")
1058 return {'CANCELLED'}
1061 # -------------------------------------------------------------
1062 # Defines button that adds an angle measure
1064 # -------------------------------------------------------------
1065 class MEASUREIT_OT_AddAngle(Operator):
1066 bl_idname = "measureit.addangle"
1067 bl_label = "Angle"
1068 bl_description = "(EDITMODE only) Add a new angle measure (select 3 vertices, 2nd is angle vertex)"
1070 # ------------------------------
1071 # Poll
1072 # ------------------------------
1073 @classmethod
1074 def poll(cls, context):
1075 o = context.object
1076 if o is None:
1077 return False
1078 else:
1079 if o.type == "MESH":
1080 if bpy.context.mode == 'EDIT_MESH':
1081 return True
1082 else:
1083 return False
1084 else:
1085 return False
1087 # ------------------------------
1088 # Execute button action
1089 # ------------------------------
1090 def execute(self, context):
1091 if context.area.type == 'VIEW_3D':
1092 # Add properties
1093 scene = context.scene
1094 mainobject = context.object
1095 mylist = get_selected_vertex_history(mainobject)
1096 if len(mylist) == 3:
1097 if 'MeasureGenerator' not in mainobject:
1098 mainobject.MeasureGenerator.add()
1100 mp = mainobject.MeasureGenerator[0]
1101 # -----------------------
1102 # Only if not exist
1103 # -----------------------
1104 if exist_segment(mp, mylist[0], mylist[1], 9, mylist[2]) is False:
1105 # Create all array elements
1106 for cont in range(len(mp.measureit_segments) - 1, mp.measureit_num):
1107 mp.measureit_segments.add()
1109 # Set values
1110 ms = mp.measureit_segments[mp.measureit_num]
1111 ms.gltype = 9
1112 ms.glpointa = mylist[0]
1113 ms.glpointb = mylist[1]
1114 ms.glpointc = mylist[2]
1115 # color
1116 ms.glcolor = scene.measureit_default_color
1117 # dist
1118 ms.glspace = scene.measureit_hint_space
1119 # text
1120 ms.gltxt = scene.measureit_gl_txt
1121 ms.glfont_size = scene.measureit_font_size
1122 ms.glfont_align = scene.measureit_font_align
1123 ms.glfont_rotat = scene.measureit_font_rotation
1124 # Add index
1125 mp.measureit_num += 1
1127 # redraw
1128 context.area.tag_redraw()
1129 return {'FINISHED'}
1130 else:
1131 self.report({'ERROR'},
1132 "MeasureIt: Select three vertices for creating angle measure")
1133 return {'FINISHED'}
1134 else:
1135 self.report({'WARNING'},
1136 "View3D not found, cannot run operator")
1138 return {'CANCELLED'}
1141 # -------------------------------------------------------------
1142 # Defines button that adds an arc measure
1144 # -------------------------------------------------------------
1145 class MEASUREIT_OT_AddArc(Operator):
1146 bl_idname = "measureit.addarc"
1147 bl_label = "Angle"
1148 bl_description = "(EDITMODE only) Add a new arc measure (select 3 vertices of the arc," \
1149 " vertices 1st and 3rd are arc extremes)"
1151 # ------------------------------
1152 # Poll
1153 # ------------------------------
1154 @classmethod
1155 def poll(cls, context):
1156 o = context.object
1157 if o is None:
1158 return False
1159 else:
1160 if o.type == "MESH":
1161 if bpy.context.mode == 'EDIT_MESH':
1162 return True
1163 else:
1164 return False
1165 else:
1166 return False
1168 # ------------------------------
1169 # Execute button action
1170 # ------------------------------
1171 def execute(self, context):
1172 if context.area.type == 'VIEW_3D':
1173 # Add properties
1174 scene = context.scene
1175 mainobject = context.object
1176 mylist = get_selected_vertex_history(mainobject)
1177 if len(mylist) == 3:
1178 if 'MeasureGenerator' not in mainobject:
1179 mainobject.MeasureGenerator.add()
1181 mp = mainobject.MeasureGenerator[0]
1182 # -----------------------
1183 # Only if not exist
1184 # -----------------------
1185 if exist_segment(mp, mylist[0], mylist[1], 11, mylist[2]) is False:
1186 # Create all array elements
1187 for cont in range(len(mp.measureit_segments) - 1, mp.measureit_num):
1188 mp.measureit_segments.add()
1190 # Set values
1191 ms = mp.measureit_segments[mp.measureit_num]
1192 ms.gltype = 11
1193 ms.glpointa = mylist[0]
1194 ms.glpointb = mylist[1]
1195 ms.glpointc = mylist[2]
1196 ms.glarrow_a = scene.measureit_glarrow_a
1197 ms.glarrow_b = scene.measureit_glarrow_b
1198 ms.glarrow_s = scene.measureit_glarrow_s
1199 # color
1200 ms.glcolor = scene.measureit_default_color
1201 # dist
1202 ms.glspace = scene.measureit_hint_space
1203 # text
1204 ms.gltxt = scene.measureit_gl_txt
1205 ms.glfont_size = scene.measureit_font_size
1206 ms.glfont_align = scene.measureit_font_align
1207 ms.glfont_rotat = scene.measureit_font_rotation
1208 # Add index
1209 mp.measureit_num += 1
1211 # redraw
1212 context.area.tag_redraw()
1213 return {'FINISHED'}
1214 else:
1215 self.report({'ERROR'},
1216 "MeasureIt: Select three vertices for creating arc measure")
1217 return {'FINISHED'}
1218 else:
1219 self.report({'WARNING'},
1220 "View3D not found, cannot run operator")
1222 return {'CANCELLED'}
1225 # -------------------------------------------------------------
1226 # Defines button that adds a label segment
1228 # -------------------------------------------------------------
1229 class MEASUREIT_OT_AddLabel(Operator):
1230 bl_idname = "measureit.addlabel"
1231 bl_label = "Add"
1232 bl_description = "(EDITMODE only) Add a new measure label (select 1 vertex)"
1234 # ------------------------------
1235 # Poll
1236 # ------------------------------
1237 @classmethod
1238 def poll(cls, context):
1239 o = context.object
1240 if o is None:
1241 return False
1242 else:
1243 if o.type == "MESH":
1244 if bpy.context.mode == 'EDIT_MESH':
1245 return True
1246 else:
1247 return False
1248 else:
1249 return False
1251 # ------------------------------
1252 # Execute button action
1253 # ------------------------------
1254 def execute(self, context):
1255 if context.area.type == 'VIEW_3D':
1256 # Add properties
1257 scene = context.scene
1258 mainobject = context.object
1259 mylist = get_selected_vertex(mainobject)
1260 if len(mylist) == 1:
1261 if 'MeasureGenerator' not in mainobject:
1262 mainobject.MeasureGenerator.add()
1264 mp = mainobject.MeasureGenerator[0]
1265 # -----------------------
1266 # Only if not exist
1267 # -----------------------
1268 if exist_segment(mp, mylist[0], mylist[0], 2) is False: # Both equal
1269 # Create all array elements
1270 for cont in range(len(mp.measureit_segments) - 1, mp.measureit_num):
1271 mp.measureit_segments.add()
1273 # Set values
1274 ms = mp.measureit_segments[mp.measureit_num]
1275 ms.gltype = 2
1276 ms.glpointa = mylist[0]
1277 ms.glpointb = mylist[0] # Equal
1278 ms.glarrow_a = scene.measureit_glarrow_a
1279 ms.glarrow_b = scene.measureit_glarrow_b
1280 ms.glarrow_s = scene.measureit_glarrow_s
1281 # color
1282 ms.glcolor = scene.measureit_default_color
1283 # dist
1284 ms.glspace = scene.measureit_hint_space
1285 # text
1286 ms.gltxt = scene.measureit_gl_txt
1287 ms.glfont_size = scene.measureit_font_size
1288 ms.glfont_align = scene.measureit_font_align
1289 ms.glfont_rotat = scene.measureit_font_rotation
1290 # Add index
1291 mp.measureit_num += 1
1293 # redraw
1294 context.area.tag_redraw()
1295 return {'FINISHED'}
1296 else:
1297 self.report({'ERROR'},
1298 "MeasureIt: Select one vertex for creating measure label")
1299 return {'FINISHED'}
1300 else:
1301 self.report({'WARNING'},
1302 "View3D not found, cannot run operator")
1304 return {'CANCELLED'}
1307 # -------------------------------------------------------------
1308 # Defines button that adds a link
1310 # -------------------------------------------------------------
1311 class MEASUREIT_OT_AddLink(Operator):
1312 bl_idname = "measureit.addlink"
1313 bl_label = "Add"
1314 bl_description = "(OBJECT mode only) Add a new measure between objects (select 2 " \
1315 "objects and optionally 1 or 2 vertices)"
1317 # ------------------------------
1318 # Poll
1319 # ------------------------------
1320 @classmethod
1321 def poll(cls, context):
1322 o = context.object
1323 if o is None:
1324 return False
1325 else:
1326 if o.type == "MESH" or o.type == "EMPTY" or o.type == "CAMERA" or o.type == "LIGHT":
1327 if bpy.context.mode == 'OBJECT':
1328 return True
1329 else:
1330 return False
1331 else:
1332 return False
1334 # ------------------------------
1335 # Execute button action
1336 # ------------------------------
1337 def execute(self, context):
1338 if context.area.type == 'VIEW_3D':
1339 scene = context.scene
1340 mainobject = context.object
1341 # -------------------------------
1342 # Verify number of objects
1343 # -------------------------------
1344 if len(context.selected_objects) != 2:
1345 self.report({'ERROR'},
1346 "MeasureIt: Select two objects only, and optionally 1 vertex or 2 vertices "
1347 "(one of each object)")
1348 return {'FINISHED'}
1349 # Locate other object
1350 linkobject = None
1351 for o in context.selected_objects:
1352 if o.name != mainobject.name:
1353 linkobject = o.name
1354 # Verify destination vertex
1355 lkobj = bpy.data.objects[linkobject]
1356 mylinkvertex = get_selected_vertex(lkobj)
1357 if len(mylinkvertex) > 1:
1358 self.report({'ERROR'},
1359 "MeasureIt: The destination object has more than one vertex selected. "
1360 "Select only 1 or none")
1361 return {'FINISHED'}
1362 # Verify origin vertex
1363 myobjvertex = get_selected_vertex(mainobject)
1364 if len(mylinkvertex) > 1:
1365 self.report({'ERROR'},
1366 "MeasureIt: The active object has more than one vertex selected. Select only 1 or none")
1367 return {'FINISHED'}
1369 # -------------------------------
1370 # Add properties
1371 # -------------------------------
1372 flag = False
1373 if 'MeasureGenerator' not in mainobject:
1374 mainobject.MeasureGenerator.add()
1376 mp = mainobject.MeasureGenerator[0]
1378 # if exist_segment(mp, mylist[0], mylist[0], 3) is False:
1379 # flag = True
1380 # Create all array elements
1381 for cont in range(len(mp.measureit_segments) - 1, mp.measureit_num):
1382 mp.measureit_segments.add()
1384 # Set values
1385 ms = mp.measureit_segments[mp.measureit_num]
1386 # -----------------------
1387 # Vertex to Vertex
1388 # -----------------------
1389 if len(myobjvertex) == 1 and len(mylinkvertex) == 1:
1390 ms.gltype = 3
1391 ms.glpointa = myobjvertex[0]
1392 ms.glpointb = mylinkvertex[0]
1393 flag = True
1394 # -----------------------
1395 # Vertex to Object
1396 # -----------------------
1397 if len(myobjvertex) == 1 and len(mylinkvertex) == 0:
1398 ms.gltype = 4
1399 ms.glpointa = myobjvertex[0]
1400 ms.glpointb = 0
1401 flag = True
1402 # -----------------------
1403 # Object to Vertex
1404 # -----------------------
1405 if len(myobjvertex) == 0 and len(mylinkvertex) == 1:
1406 ms.gltype = 5
1407 ms.glpointa = 0
1408 ms.glpointb = mylinkvertex[0]
1409 flag = True
1410 # -----------------------
1411 # Object to Object
1412 # -----------------------
1413 if len(myobjvertex) == 0 and len(mylinkvertex) == 0:
1414 ms.gltype = 8
1415 ms.glpointa = 0
1416 ms.glpointb = 0 # Equal
1417 flag = True
1419 # ------------------
1420 # only if created
1421 # ------------------
1422 if flag is True:
1423 ms.glarrow_a = scene.measureit_glarrow_a
1424 ms.glarrow_b = scene.measureit_glarrow_b
1425 ms.glarrow_s = scene.measureit_glarrow_s
1426 # color
1427 ms.glcolor = scene.measureit_default_color
1428 # dist
1429 ms.glspace = scene.measureit_hint_space
1430 # text
1431 ms.gltxt = scene.measureit_gl_txt
1432 ms.glfont_size = scene.measureit_font_size
1433 ms.glfont_align = scene.measureit_font_align
1434 ms.glfont_rotat = scene.measureit_font_rotation
1435 # link
1436 ms.gllink = linkobject
1437 # Add index
1438 mp.measureit_num += 1
1440 # -----------------------
1441 # Only if not exist
1442 # -----------------------
1443 # redraw
1444 context.area.tag_redraw()
1445 return {'FINISHED'}
1446 else:
1447 self.report({'WARNING'},
1448 "View3D not found, cannot run operator")
1450 return {'CANCELLED'}
1453 # -------------------------------------------------------------
1454 # Defines button that adds an origin segment
1456 # -------------------------------------------------------------
1457 class MEASUREIT_OT_AddOrigin(Operator):
1458 bl_idname = "measureit.addorigin"
1459 bl_label = "Add"
1460 bl_description = "(OBJECT mode only) Add a new measure to origin (select object and optionally 1 vertex)"
1462 # ------------------------------
1463 # Poll
1464 # ------------------------------
1465 @classmethod
1466 def poll(cls, context):
1467 o = context.object
1468 if o is None:
1469 return False
1470 else:
1471 if o.type == "MESH" or o.type == "EMPTY" or o.type == "CAMERA" or o.type == "LIGHT":
1472 if bpy.context.mode == 'OBJECT':
1473 return True
1474 else:
1475 return False
1476 else:
1477 return False
1479 # ------------------------------
1480 # Execute button action
1481 # ------------------------------
1482 def execute(self, context):
1483 if context.area.type == 'VIEW_3D':
1484 # Add properties
1485 scene = context.scene
1486 mainobject = context.object
1487 mylist = get_selected_vertex(mainobject)
1488 if 'MeasureGenerator' not in mainobject:
1489 mainobject.MeasureGenerator.add()
1491 mp = mainobject.MeasureGenerator[0]
1492 # Create all array elements
1493 for cont in range(len(mp.measureit_segments) - 1, mp.measureit_num):
1494 mp.measureit_segments.add()
1496 # -----------------------
1497 # Set values
1498 # -----------------------
1499 ms = mp.measureit_segments[mp.measureit_num]
1500 flag = False
1501 if len(mylist) > 0:
1502 if len(mylist) == 1:
1503 if exist_segment(mp, mylist[0], mylist[0], 6) is False: # Both equal
1504 flag = True
1505 # Vertex to origin
1506 ms.gltype = 6
1507 ms.glpointa = mylist[0]
1508 ms.glpointb = mylist[0]
1509 else:
1510 self.report({'ERROR'},
1511 "MeasureIt: Enter in EDITMODE and select one vertex only for creating "
1512 "measure from vertex to origin")
1513 return {'FINISHED'}
1514 else:
1515 # Object to origin
1516 if exist_segment(mp, 0, 0, 7) is False: # Both equal
1517 flag = True
1518 ms.gltype = 7
1519 ms.glpointa = 0
1520 ms.glpointb = 0
1521 # ------------------
1522 # only if created
1523 # ------------------
1524 if flag is True:
1525 ms.glarrow_a = scene.measureit_glarrow_a
1526 ms.glarrow_b = scene.measureit_glarrow_b
1527 ms.glarrow_s = scene.measureit_glarrow_s
1528 # color
1529 ms.glcolor = scene.measureit_default_color
1530 # dist
1531 ms.glspace = scene.measureit_hint_space
1532 # text
1533 ms.gltxt = scene.measureit_gl_txt
1534 ms.glfont_size = scene.measureit_font_size
1535 ms.glfont_align = scene.measureit_font_align
1536 ms.glfont_rotat = scene.measureit_font_rotation
1537 # Add index
1538 mp.measureit_num += 1
1540 # redraw
1541 context.area.tag_redraw()
1543 return {'FINISHED'}
1544 else:
1545 self.report({'WARNING'},
1546 "View3D not found, cannot run operator")
1548 return {'CANCELLED'}
1551 # -------------------------------------------------------------
1552 # Defines button that deletes a measure segment
1554 # -------------------------------------------------------------
1555 class MEASUREIT_OT_DeleteSegment(Operator):
1556 bl_idname = "measureit.deletesegment"
1557 bl_label = "Delete"
1558 bl_description = "Delete a measure"
1559 tag: IntProperty()
1561 # ------------------------------
1562 # Execute button action
1563 # ------------------------------
1564 def execute(self, context):
1565 if context.area.type == 'VIEW_3D':
1566 # Add properties
1567 mainobject = context.object
1568 mp = mainobject.MeasureGenerator[0]
1569 ms = mp.measureit_segments[self.tag]
1570 ms.glfree = True
1571 # Delete element
1572 mp.measureit_segments.remove(self.tag)
1573 mp.measureit_num -= 1
1574 # redraw
1575 context.area.tag_redraw()
1576 return {'FINISHED'}
1577 else:
1578 self.report({'WARNING'},
1579 "View3D not found, cannot run operator")
1581 return {'CANCELLED'}
1584 # -------------------------------------------------------------
1585 # Defines button that deletes all measure segments
1587 # -------------------------------------------------------------
1588 class MEASUREIT_OT_DeleteAllSegment(Operator):
1589 bl_idname = "measureit.deleteallsegment"
1590 bl_label = "Delete"
1591 bl_description = "Delete all measures (it cannot be undone)"
1592 tag: IntProperty()
1594 # ------------------------------
1595 # Execute button action
1596 # ------------------------------
1597 def execute(self, context):
1598 if context.area.type == 'VIEW_3D':
1599 # Add properties
1600 mainobject = context.object
1601 mp = mainobject.MeasureGenerator[0]
1603 while len(mp.measureit_segments) > 0:
1604 mp.measureit_segments.remove(0)
1606 # reset size
1607 mp.measureit_num = len(mp.measureit_segments)
1608 # redraw
1609 context.area.tag_redraw()
1610 return {'FINISHED'}
1611 else:
1612 self.report({'WARNING'},
1613 "View3D not found, cannot run operator")
1615 return {'CANCELLED'}
1618 # -------------------------------------------------------------
1619 # Defines button that deletes all measure segment sums
1621 # -------------------------------------------------------------
1622 class MEASUREIT_OT_DeleteAllSum(Operator):
1623 bl_idname = "measureit.deleteallsum"
1624 bl_label = "Delete"
1625 bl_description = "Delete all sum groups"
1626 tag: IntProperty()
1628 # ------------------------------
1629 # Execute button action
1630 # ------------------------------
1631 # noinspection PyMethodMayBeStatic
1632 def execute(self, context):
1633 if context.object is not None:
1634 if 'MeasureGenerator' in context.object:
1635 mp = context.object.MeasureGenerator[0]
1636 for idx in range(mp.measureit_num):
1637 ms = mp.measureit_segments[idx]
1638 ms.gltot = '99'
1640 return {'FINISHED'}
1643 # -------------------------------------------------------------
1644 # Defines button that expands all measure segments
1646 # -------------------------------------------------------------
1647 class MEASUREIT_OT_ExpandAllSegment(Operator):
1648 bl_idname = "measureit.expandallsegment"
1649 bl_label = "Expand"
1650 bl_description = "Expand all measure properties"
1651 tag: IntProperty()
1653 # ------------------------------
1654 # Execute button action
1655 # ------------------------------
1656 def execute(self, context):
1657 if context.area.type == 'VIEW_3D':
1658 # Add properties
1659 mainobject = context.object
1660 mp = mainobject.MeasureGenerator[0]
1662 for i in mp.measureit_segments:
1663 i.gladvance = True
1665 return {'FINISHED'}
1666 else:
1667 self.report({'WARNING'},
1668 "View3D not found, cannot run operator")
1670 return {'CANCELLED'}
1673 # -------------------------------------------------------------
1674 # Defines button that collapses all measure segments
1676 # -------------------------------------------------------------
1677 class MEASUREIT_OT_CollapseAllSegment(Operator):
1678 bl_idname = "measureit.collapseallsegment"
1679 bl_label = "Collapse"
1680 bl_description = "Collapses all measure properties"
1681 tag: IntProperty()
1683 # ------------------------------
1684 # Execute button action
1685 # ------------------------------
1686 def execute(self, context):
1687 if context.area.type == 'VIEW_3D':
1688 # Add properties
1689 mainobject = context.object
1690 mp = mainobject.MeasureGenerator[0]
1692 for i in mp.measureit_segments:
1693 i.gladvance = False
1695 return {'FINISHED'}
1696 else:
1697 self.report({'WARNING'},
1698 "View3D not found, cannot run operator")
1700 return {'CANCELLED'}
1703 # -------------------------------------------------------------
1704 # Defines button for render option
1706 # -------------------------------------------------------------
1707 class MEASUREIT_OT_RenderSegment(Operator):
1708 bl_idname = "measureit.rendersegment"
1709 bl_label = "Render"
1710 bl_description = "Create a render image with measures. Use UV/Image editor to view image generated"
1711 tag: IntProperty()
1713 # ------------------------------
1714 # Execute button action
1715 # ------------------------------
1716 # noinspection PyMethodMayBeStatic,PyUnusedLocal
1717 def execute(self, context):
1718 scene = context.scene
1719 msg = "New image created with measures. Open it in UV/image editor"
1720 camera_msg = "Unable to render. No camera found"
1721 # -----------------------------
1722 # Check camera
1723 # -----------------------------
1724 if scene.camera is None:
1725 self.report({'ERROR'}, camera_msg)
1726 return {'FINISHED'}
1727 # -----------------------------
1728 # Frame render
1729 # -----------------------------
1730 if scene.measureit_render_type == "1":
1731 # noinspection PyBroadException
1732 if render_main(self, context) is True:
1733 self.report({'INFO'}, msg)
1734 # -----------------------------
1735 # Animation
1736 # -----------------------------
1737 if scene.measureit_render_type == "2":
1738 oldframe = scene.frame_current
1739 flag = False
1740 # loop frames
1741 for frm in range(scene.frame_start, scene.frame_end + 1):
1742 scene.frame_set(frm)
1743 print("MeasureIt: Rendering frame %04d" % frm)
1744 flag = render_main(self, context, True)
1745 if flag is False:
1746 break
1748 scene.frame_current = oldframe
1749 if flag is True:
1750 self.report({'INFO'}, msg)
1752 return {'FINISHED'}
1754 # ---------------------
1755 # Set cameraView
1756 # ---------------------
1757 # noinspection PyMethodMayBeStatic
1758 def set_camera_view(self):
1759 for area in bpy.context.screen.areas:
1760 if area.type == 'VIEW_3D':
1761 area.spaces[0].region_3d.view_perspective = 'CAMERA'
1763 # -------------------------------------
1764 # Set only render status
1765 # -------------------------------------
1766 # noinspection PyMethodMayBeStatic
1767 def set_only_render(self, status):
1768 screen = bpy.context.screen
1770 v3d = False
1771 s = None
1772 # get spaceview_3d in current screen
1773 for a in screen.areas:
1774 if a.type == 'VIEW_3D':
1775 for s in a.spaces:
1776 if s.type == 'VIEW_3D':
1777 v3d = s
1778 break
1780 if v3d is not False:
1781 s.show_only_render = status
1784 # -------------------------------------------------------------
1785 # Defines a new note
1787 # -------------------------------------------------------------
1788 class MEASUREIT_OT_AddNote(Operator):
1789 bl_idname = "measureit.addnote"
1790 bl_label = "Note"
1791 bl_description = "(OBJECT mode only) Add a new annotation"
1792 tag: IntProperty()
1794 # ------------------------------
1795 # Poll
1796 # ------------------------------
1797 # noinspection PyUnusedLocal
1798 @classmethod
1799 def poll(cls, context):
1800 if bpy.context.mode == 'OBJECT':
1801 return True
1802 else:
1803 return False
1805 # ------------------------------
1806 # Execute button action
1807 # ------------------------------
1808 def execute(self, context):
1809 if context.area.type == 'VIEW_3D':
1810 bpy.ops.object.empty_add(type='PLAIN_AXES')
1811 myempty = bpy.data.objects[bpy.context.active_object.name]
1812 myempty.location = bpy.context.scene.cursor.location
1813 myempty.empty_display_size = 0.01
1814 myempty.name = "Annotation"
1815 # Add properties
1816 scene = context.scene
1817 mainobject = myempty
1818 if 'MeasureGenerator' not in mainobject:
1819 mainobject.MeasureGenerator.add()
1821 mp = mainobject.MeasureGenerator[0]
1822 # Create all array elements
1823 for cont in range(len(mp.measureit_segments) - 1, mp.measureit_num):
1824 mp.measureit_segments.add()
1826 # Set values
1827 ms = mp.measureit_segments[mp.measureit_num]
1828 ms.gltype = 10
1829 ms.glpointa = 0
1830 ms.glpointb = 0 # Equal
1831 # color
1832 ms.glcolor = scene.measureit_default_color
1833 # dist
1834 ms.glspace = scene.measureit_hint_space
1835 # text
1836 ms.gltxt = scene.measureit_gl_txt
1837 ms.glfont_size = scene.measureit_font_size
1838 ms.glfont_align = scene.measureit_font_align
1839 ms.glfont_rotat = scene.measureit_font_rotation
1840 # Add index
1841 mp.measureit_num += 1
1843 # redraw
1844 context.area.tag_redraw()
1845 return {'FINISHED'}
1846 else:
1847 self.report({'WARNING'},
1848 "View3D not found, cannot run operator")
1850 return {'CANCELLED'}
1853 # -------------------------------------------------------------
1854 # Defines button that enables/disables the tip display
1856 # -------------------------------------------------------------
1857 class MEASUREIT_OT_RunHintDisplay(Operator):
1858 bl_idname = "measureit.runopengl"
1859 bl_label = "Display hint data manager"
1860 bl_description = "Main control for enabling or disabling the display of measurements in the viewport"
1862 _handle = None # keep function handler
1864 # ----------------------------------
1865 # Enable gl drawing adding handler
1866 # ----------------------------------
1867 @staticmethod
1868 def handle_add(self, context):
1869 if MEASUREIT_OT_RunHintDisplay._handle is None:
1870 MEASUREIT_OT_RunHintDisplay._handle = SpaceView3D.draw_handler_add(draw_callback_px, (self, context),
1871 'WINDOW',
1872 'POST_PIXEL')
1873 context.window_manager.measureit_run_opengl = True
1875 # ------------------------------------
1876 # Disable gl drawing removing handler
1877 # ------------------------------------
1878 # noinspection PyUnusedLocal
1879 @staticmethod
1880 def handle_remove(self, context):
1881 if MEASUREIT_OT_RunHintDisplay._handle is not None:
1882 SpaceView3D.draw_handler_remove(MEASUREIT_OT_RunHintDisplay._handle, 'WINDOW')
1883 MEASUREIT_OT_RunHintDisplay._handle = None
1884 context.window_manager.measureit_run_opengl = False
1886 # ------------------------------
1887 # Execute button action
1888 # ------------------------------
1889 def execute(self, context):
1890 if context.area.type == 'VIEW_3D':
1891 if context.window_manager.measureit_run_opengl is False:
1892 self.handle_add(self, context)
1893 context.area.tag_redraw()
1894 else:
1895 self.handle_remove(self, context)
1896 context.area.tag_redraw()
1898 return {'FINISHED'}
1899 else:
1900 self.report({'WARNING'},
1901 "View3D not found, cannot run operator")
1903 return {'CANCELLED'}
1906 # -------------------------------------------------------------
1907 # Handle all draw routines (OpenGL main entry point)
1909 # -------------------------------------------------------------
1910 def draw_main(context):
1911 region = bpy.context.region
1912 # Detect if Quadview to get drawing area
1913 if not context.space_data.region_quadviews:
1914 rv3d = bpy.context.space_data.region_3d
1915 else:
1916 # verify area
1917 if context.area.type != 'VIEW_3D' or context.space_data.type != 'VIEW_3D':
1918 return
1919 i = -1
1920 for region in context.area.regions:
1921 if region.type == 'WINDOW':
1922 i += 1
1923 if context.region.id == region.id:
1924 break
1925 else:
1926 return
1928 rv3d = context.space_data.region_quadviews[i]
1930 scene = bpy.context.scene
1932 # Display selected or all
1933 if scene.measureit_gl_ghost is False:
1934 objlist = context.selected_objects
1935 else:
1936 objlist = context.view_layer.objects
1938 # Enable GL drawing
1939 bgl.glEnable(bgl.GL_BLEND)
1940 # ---------------------------------------
1941 # Generate all OpenGL calls for measures
1942 # ---------------------------------------
1943 for myobj in objlist:
1944 if myobj.visible_get() is True:
1945 if 'MeasureGenerator' in myobj:
1946 op = myobj.MeasureGenerator[0]
1947 draw_segments(context, myobj, op, region, rv3d)
1949 # ---------------------------------------
1950 # Generate all OpenGL calls for debug
1951 # ---------------------------------------
1952 if scene.measureit_debug is True:
1953 selobj = bpy.context.selected_objects
1954 for myobj in selobj:
1955 if scene.measureit_debug_objects is True:
1956 draw_object(context, myobj, region, rv3d)
1957 elif scene.measureit_debug_object_loc is True:
1958 draw_object(context, myobj, region, rv3d)
1959 if scene.measureit_debug_vertices is True:
1960 draw_vertices(context, myobj, region, rv3d)
1961 elif scene.measureit_debug_vert_loc is True:
1962 draw_vertices(context, myobj, region, rv3d)
1963 if scene.measureit_debug_edges is True:
1964 draw_edges(context, myobj, region, rv3d)
1965 if scene.measureit_debug_faces is True or scene.measureit_debug_normals is True:
1966 draw_faces(context, myobj, region, rv3d)
1968 # -----------------------
1969 # restore opengl defaults
1970 # -----------------------
1971 bgl.glDisable(bgl.GL_BLEND)
1974 # -------------------------------------------------------------
1975 # Handler for drawing OpenGl
1976 # -------------------------------------------------------------
1977 # noinspection PyUnusedLocal
1978 def draw_callback_px(self, context):
1979 draw_main(context)
1982 # -------------------------------------------------------------
1983 # Check if the segment already exist
1985 # -------------------------------------------------------------
1986 def exist_segment(mp, pointa, pointb, typ=1, pointc=None):
1987 # for ms in mp.measureit_segments[mp.measureit_num]
1988 for ms in mp.measureit_segments:
1989 if ms.gltype == typ and ms.glfree is False:
1990 if typ != 9:
1991 if ms.glpointa == pointa and ms.glpointb == pointb:
1992 return True
1993 if ms.glpointa == pointb and ms.glpointb == pointa:
1994 return True
1995 else:
1996 if ms.glpointa == pointa and ms.glpointb == pointb and ms.glpointc == pointc:
1997 return True
1999 return False
2002 # -------------------------------------------------------------
2003 # Get vertex selected
2004 # -------------------------------------------------------------
2005 def get_selected_vertex(myobject):
2006 mylist = []
2007 # if not mesh, no vertex
2008 if myobject.type != "MESH":
2009 return mylist
2010 # --------------------
2011 # meshes
2012 # --------------------
2013 oldobj = bpy.context.object
2014 bpy.context.view_layer.objects.active = myobject
2015 flag = False
2016 if myobject.mode != 'EDIT':
2017 bpy.ops.object.mode_set(mode='EDIT')
2018 flag = True
2020 bm = from_edit_mesh(myobject.data)
2021 tv = len(bm.verts)
2022 for v in bm.verts:
2023 if v.select:
2024 mylist.append(v.index)
2026 if flag is True:
2027 bpy.ops.object.editmode_toggle()
2028 # Back context object
2029 bpy.context.view_layer.objects.active = oldobj
2031 # if select all vertices, then use origin
2032 if tv == len(mylist):
2033 return []
2035 return mylist
2038 # -------------------------------------------------------------
2039 # Get vertex selected
2040 # -------------------------------------------------------------
2041 def get_selected_vertex_history(myobject):
2042 mylist = []
2043 # if not mesh, no vertex
2044 if myobject.type != "MESH":
2045 return mylist
2046 # --------------------
2047 # meshes
2048 # --------------------
2049 oldobj = bpy.context.object
2050 bpy.context.view_layer.objects.active = myobject
2051 flag = False
2052 if myobject.mode != 'EDIT':
2053 bpy.ops.object.mode_set(mode='EDIT')
2054 flag = True
2056 bm = from_edit_mesh(myobject.data)
2057 for v in bm.select_history:
2058 mylist.append(v.index)
2060 if flag is True:
2061 bpy.ops.object.editmode_toggle()
2062 # Back context object
2063 bpy.context.view_layer.objects.active = oldobj
2065 return mylist
2068 # -------------------------------------------------------------
2069 # Get vertex selected segments
2070 # -------------------------------------------------------------
2071 def get_smart_selected(myobject):
2072 mylist = []
2073 # if not mesh, no vertex
2074 if myobject.type != "MESH":
2075 return mylist
2076 # --------------------
2077 # meshes
2078 # --------------------
2079 oldobj = bpy.context.object
2080 bpy.context.view_layer.objects.active = myobject
2081 flag = False
2082 if myobject.mode != 'EDIT':
2083 bpy.ops.object.mode_set(mode='EDIT')
2084 flag = True
2086 bm = from_edit_mesh(myobject.data)
2087 for e in bm.edges:
2088 if e.select is True:
2089 mylist.append(e.verts[0].index)
2090 mylist.append(e.verts[1].index)
2092 if flag is True:
2093 bpy.ops.object.editmode_toggle()
2094 # Back context object
2095 bpy.context.view_layer.objects.active = oldobj
2097 return mylist
2100 # -------------------------------------------------------------
2101 # Get vertex selected faces
2102 # -------------------------------------------------------------
2103 def get_selected_faces(myobject):
2104 mylist = []
2105 # if not mesh, no vertex
2106 if myobject.type != "MESH":
2107 return mylist
2108 # --------------------
2109 # meshes
2110 # --------------------
2111 oldobj = bpy.context.object
2112 bpy.context.view_layer.objects.active = myobject
2113 flag = False
2114 if myobject.mode != 'EDIT':
2115 bpy.ops.object.mode_set(mode='EDIT')
2116 flag = True
2118 bm = from_edit_mesh(myobject.data)
2119 for e in bm.faces:
2120 myfaces = []
2121 if e.select is True:
2122 for i in range(len(e.verts)):
2123 myfaces.append(e.verts[i].index)
2125 mylist.extend([myfaces])
2127 if flag is True:
2128 bpy.ops.object.editmode_toggle()
2129 # Back context object
2130 bpy.context.view_layer.objects.active = oldobj
2132 return mylist