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