# SPDX-License-Identifier: GPL-2.0-or-later # ---------------------------------------------------------- # File: measureit_main.py # Main panel for different Measureit general actions # Author: Antonio Vazquez (antonioya) # # ---------------------------------------------------------- # noinspection PyUnresolvedReferences import bpy import bmesh from bmesh import from_edit_mesh # noinspection PyUnresolvedReferences import bgl from bpy.types import PropertyGroup, Panel, Object, Operator, SpaceView3D from bpy.props import IntProperty, CollectionProperty, FloatVectorProperty, BoolProperty, StringProperty, \ FloatProperty, EnumProperty from bpy.app.handlers import persistent # noinspection PyUnresolvedReferences from .measureit_geometry import * from .measureit_render import * # ------------------------------------------------------ # Handler to detect new Blend load # # ------------------------------------------------------ # noinspection PyUnusedLocal @persistent def load_handler(dummy): MEASUREIT_OT_RunHintDisplay.handle_remove(None, bpy.context) # ------------------------------------------------------ # Handler to detect save Blend # Clear not used measured # # ------------------------------------------------------ # noinspection PyUnusedLocal @persistent def save_handler(dummy): # noinspection PyBroadException try: print("MeasureIt: Cleaning data") objlist = bpy.context.scene.objects for myobj in objlist: if 'MeasureGenerator' in myobj: mp = myobj.MeasureGenerator[0] x = 0 for ms in mp.measureit_segments: ms.name = "segment_" + str(x) x += 1 if ms.glfree is True: idx = mp.measureit_segments.find(ms.name) if idx > -1: print("MeasureIt: Removed segment not used") mp.measureit_segments.remove(idx) # reset size mp.measureit_num = len(mp.measureit_segments) except: pass bpy.app.handlers.load_post.append(load_handler) bpy.app.handlers.save_pre.append(save_handler) # ------------------------------------------------------------------ # Define property group class for measureit faces index # ------------------------------------------------------------------ class MeasureitIndex(PropertyGroup): glidx: IntProperty(name="index", description="vertex index") # Register bpy.utils.register_class(MeasureitIndex) # ------------------------------------------------------------------ # Define property group class for measureit faces # ------------------------------------------------------------------ class MeasureitFaces(PropertyGroup): glface: IntProperty(name="glface", description="Face number") # Array of index measureit_index: CollectionProperty(type=MeasureitIndex) # Register bpy.utils.register_class(MeasureitFaces) # ------------------------------------------------------------------ # Define property group class for measureit data # ------------------------------------------------------------------ class MeasureitProperties(PropertyGroup): gltype: IntProperty(name="gltype", description="Measure type (1-Segment, 2-Label, etc..)", default=1) glpointa: IntProperty(name="glpointa", description="Hidden property for opengl") glpointb: IntProperty(name="glpointb", description="Hidden property for opengl") glpointc: IntProperty(name="glpointc", description="Hidden property for opengl") glcolor: FloatVectorProperty(name="glcolor", description="Color for the measure", default=(0.173, 0.545, 1.0, 1.0), min=0.1, max=1, subtype='COLOR', size=4) glview: BoolProperty(name="glview", description="Measure visible/hide", default=True) glspace: FloatProperty(name='glspace', min=-100, max=100, default=0.1, precision=3, description='Distance to display measure') glwidth: IntProperty(name='glwidth', min=1, max=10, default=1, description='line width') glfree: BoolProperty(name="glfree", description="This measure is free and can be deleted", default=False) gltxt: StringProperty(name="gltxt", maxlen=256, description="Short description (use | for line break)") gladvance: BoolProperty(name="gladvance", description="Advanced options as line width or position", default=False) gldefault: BoolProperty(name="gldefault", description="Display measure in position calculated by default", default=True) glnormalx: FloatProperty(name="glnormalx", description="Change orientation in X axis", default=1, min=-1, max=1, precision=2) glnormaly: FloatProperty(name="glnormaly", description="Change orientation in Y axis", default=0, min=-1, max=1, precision=2) glnormalz: FloatProperty(name="glnormalz", description="Change orientation in Z axis", default=0, min=-1, max=1, precision=2) glfont_size: IntProperty(name="Text Size", description="Text size", default=14, min=6, max=150) glfont_align: EnumProperty(items=(('L', "Left Align", ""), ('C', "Center Align", ""), ('R', "Right Align", "")), name="Align Font", description="Set Font Alignment") glfont_rotat: IntProperty(name='Rotate', min=0, max=360, default=0, description="Text rotation in degrees") gllink: StringProperty(name="gllink", description="linked object for linked measures") glocwarning: BoolProperty(name="glocwarning", description="Display a warning if some axis is not used in distance", default=True) glocx: BoolProperty(name="glocx", description="Include changes in X axis for calculating the distance", default=True) glocy: BoolProperty(name="glocy", description="Include changes in Y axis for calculating the distance", default=True) glocz: BoolProperty(name="glocz", description="Include changes in Z axis for calculating the distance", default=True) glfontx: IntProperty(name="glfontx", description="Change font position in X axis", default=0, min=-3000, max=3000) glfonty: IntProperty(name="glfonty", description="Change font position in Y axis", default=0, min=-3000, max=3000) gldist: BoolProperty(name="gldist", description="Display distance for this measure", default=True) glnames: BoolProperty(name="glnames", description="Display text for this measure", default=True) gltot: EnumProperty(items=(('99', "-", "Select a group for sum"), ('0', "A", ""), ('1', "B", ""), ('2', "C", ""), ('3', "D", ""), ('4', "E", ""), ('5', "F", ""), ('6', "G", ""), ('7', "H", ""), ('8', "I", ""), ('9', "J", ""), ('10', "K", ""), ('11', "L", ""), ('12', "M", ""), ('13', "N", ""), ('14', "O", ""), ('15', "P", ""), ('16', "Q", ""), ('17', "R", ""), ('18', "S", ""), ('19', "T", ""), ('20', "U", ""), ('21', "V", ""), ('22', "W", ""), ('23', "X", ""), ('24', "Y", ""), ('25', "Z", "")), name="Sum in Group", description="Add segment length in selected group") glorto: EnumProperty(items=(('99', "None", ""), ('0', "A", "Point A must use selected point B location"), ('1', "B", "Point B must use selected point A location")), name="Orthogonal", description="Display point selected as orthogonal (select axis to copy)") glorto_x: BoolProperty(name="ox", description="Copy X location", default=False) glorto_y: BoolProperty(name="oy", description="Copy Y location", default=False) glorto_z: BoolProperty(name="oz", description="Copy Z location", default=False) glarrow_a: EnumProperty(items=(('99', "--", "No arrow"), ('1', "Line", "The point of the arrow are lines"), ('2', "Triangle", "The point of the arrow is triangle"), ('3', "TShape", "The point of the arrow is a T")), name="A end", description="Add arrows to point A") glarrow_b: EnumProperty(items=(('99', "--", "No arrow"), ('1', "Line", "The point of the arrow are lines"), ('2', "Triangle", "The point of the arrow is triangle"), ('3', "TShape", "The point of the arrow is a T")), name="B end", description="Add arrows to point B") glarrow_s: IntProperty(name="Size", description="Arrow size", default=15, min=6, max=500) glarc_full: BoolProperty(name="arcfull", description="Create full circunference", default=False) glarc_extrad: BoolProperty(name="arcextrad", description="Adapt radio length to arc line", default=True) glarc_rad: BoolProperty(name="arc rad", description="Show arc radius", default=True) glarc_len: BoolProperty(name="arc len", description="Show arc length", default=True) glarc_ang: BoolProperty(name="arc ang", description="Show arc angle", default=True) glarc_a: EnumProperty(items=(('99', "--", "No arrow"), ('1', "Line", "The point of the arrow are lines"), ('2', "Triangle", "The point of the arrow is triangle"), ('3', "TShape", "The point of the arrow is a T")), name="Ar end", description="Add arrows to point A") glarc_b: EnumProperty(items=(('99', "--", "No arrow"), ('1', "Line", "The point of the arrow are lines"), ('2', "Triangle", "The point of the arrow is triangle"), ('3', "TShape", "The point of the arrow is a T")), name="Br end", description="Add arrows to point B") glarc_s: IntProperty(name="Size", description="Arrow size", default=15, min=6, max=500) glarc_txradio: StringProperty(name="txradio", description="Text for radius", default="r=") glarc_txlen: StringProperty(name="txlen", description="Text for length", default="L=") glarc_txang: StringProperty(name="txang", description="Text for angle", default="A=") glcolorarea: FloatVectorProperty(name="glcolorarea", description="Color for the measure of area", default=(0.1, 0.1, 0.1, 1.0), min=0.1, max=1, subtype='COLOR', size=4) # Array of faces measureit_faces: CollectionProperty(type=MeasureitFaces) # Register bpy.utils.register_class(MeasureitProperties) # ------------------------------------------------------------------ # Define object class (container of segments) # Measureit # ------------------------------------------------------------------ class MeasureContainer(PropertyGroup): measureit_num: IntProperty(name='Number of measures', min=0, max=1000, default=0, description='Number total of measureit elements') # Array of segments measureit_segments: CollectionProperty(type=MeasureitProperties) bpy.utils.register_class(MeasureContainer) Object.MeasureGenerator = CollectionProperty(type=MeasureContainer) # ------------------------------------------------------------------ # Define UI class # Measureit # ------------------------------------------------------------------ class MEASUREIT_PT_Edit(Panel): bl_idname = "MEASUREIT_PT_Edit" bl_label = "Items" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category= 'View' bl_parent_id = 'MEASUREIT_PT_Main' # ----------------------------------------------------- # Verify if visible # ----------------------------------------------------- @classmethod def poll(cls, context): o = context.object if o is None: return False if 'MeasureGenerator' not in o: return False else: mp = context.object.MeasureGenerator[0] if mp.measureit_num > 0: return True else: return False # ----------------------------------------------------- # Draw (create UI interface) # ----------------------------------------------------- # noinspection PyUnusedLocal def draw(self, context): layout = self.layout scene = context.scene if context.object is not None: if 'MeasureGenerator' in context.object: box = layout.box() row = box.row() row.label(text=context.object.name) row = box.row() row.prop(scene, 'measureit_gl_precision', text="Precision") row.prop(scene, 'measureit_units') row = box.row() row.prop(scene, 'measureit_gl_show_d', text="Distances", toggle=True, icon="ALIGN_CENTER") row.prop(scene, 'measureit_gl_show_n', text="Texts", toggle=True, icon="FONT_DATA") row = box.row() row.prop(scene, 'measureit_hide_units', text="Hide measurement unit") # Scale factor row = box.row() row.prop(scene, 'measureit_scale', text="Scale") if scene.measureit_scale is True: split = row.split(factor=0.25, align=False) split.prop(scene, 'measureit_scale_color', text="") split.prop(scene, 'measureit_scale_factor', text="1") row = box.row() row.separator() row.prop(scene, 'measureit_gl_scaletxt', text="") row.prop(scene, 'measureit_scale_font') row.prop(scene, 'measureit_scale_precision', text="") row = box.row() row.separator() row.prop(scene, 'measureit_scale_pos_x') row.prop(scene, 'measureit_scale_pos_y') # Override row = box.row() row.prop(scene, 'measureit_ovr', text="Override") if scene.measureit_ovr is True: split = row.split(factor=0.25, align=False) split.prop(scene, 'measureit_ovr_color', text="") split.prop(scene, 'measureit_ovr_width', text="Width") row = box.row() row.separator() row.prop(scene, 'measureit_ovr_font', text="Font") row.prop(scene, 'measureit_ovr_font_align', text="") if scene.measureit_ovr_font_align == 'L': row.prop(scene, 'measureit_ovr_font_rotation', text="Rotate") mp = context.object.MeasureGenerator[0] # ----------------- # loop # ----------------- if mp.measureit_num > 0: box = layout.box() row = box.row(align=True) row.operator("measureit.expandallsegment", text="Expand all", icon="ZOOM_IN") row.operator("measureit.collapseallsegment", text="Collapse all", icon="ZOOM_OUT") for idx in range(mp.measureit_num): if mp.measureit_segments[idx].glfree is False: add_item(box, idx, mp.measureit_segments[idx]) row = box.row() row.operator("measureit.deleteallsegment", text="Delete all", icon="X") # ----------------- # Sum loop segments # ----------------- if mp.measureit_num > 0: scale = bpy.context.scene.unit_settings.scale_length tx = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"] tot = [0.0] * len(tx) ac = [False] * len(tx) myobj = context.object obverts = get_mesh_vertices(myobj) viewtot = False for idx in range(mp.measureit_num): ms = mp.measureit_segments[idx] if (ms.gltype == 1 or ms.gltype == 12 or ms.gltype == 13 or ms.gltype == 14) and ms.gltot != '99' \ and ms.glfree is False: # only segments if bpy.context.mode == "EDIT_MESH": bm = bmesh.from_edit_mesh(bpy.context.edit_object.data) if hasattr(bm.verts, "ensure_lookup_table"): bm.verts.ensure_lookup_table() if ms.glpointa <= len(obverts) and ms.glpointb <= len(obverts): p1 = get_point(obverts[ms.glpointa].co, myobj) if ms.gltype == 1: p2 = get_point(obverts[ms.glpointb].co, myobj) elif ms.gltype == 12: p2 = get_point((0.0, obverts[ms.glpointa].co[1], obverts[ms.glpointa].co[2]), myobj) elif ms.gltype == 13: p2 = get_point((obverts[ms.glpointa].co[0], 0.0, obverts[ms.glpointa].co[2]), myobj) else: p2 = get_point((obverts[ms.glpointa].co[0], obverts[ms.glpointa].co[1], 0.0), myobj) dist, distloc = distance(p1, p2, ms.glocx, ms.glocy, ms.glocz) if dist == distloc: usedist = dist else: usedist = distloc usedist *= scale tot[int(ms.gltot)] += usedist ac[int(ms.gltot)] = True viewtot = True # ----------------- # Print values # ----------------- if viewtot is True: pr = scene.measureit_gl_precision fmt = "%1." + str(pr) + "f" units = scene.measureit_units box = layout.box() box.label(text="Totals", icon='SOLO_ON') final = 0 for idx in range(len(tot)): if ac[idx] is True: final += tot[idx] tx_dist = format_distance(fmt, units, tot[idx]) row = box.row(align=True) row.label(text="Group " + tx[idx] + ":") row.label(text=" ") row.label(text=tx_dist) # Grand total row = box.row(align=True) row.label(text="") row.label(text=" ") row.label(text="-" * 20) tx_dist = format_distance(fmt, units, final) row = box.row(align=True) row.label(text="") row.label(text=" ") row.label(text=tx_dist) # delete all row = box.row() row.operator("measureit.deleteallsum", text="Delete all", icon="X") # ----------------------------------------------------- # Add segment options to the panel. # ----------------------------------------------------- def add_item(box, idx, segment): scene = bpy.context.scene row = box.row(align=True) if segment.glview is True: icon = "HIDE_OFF" else: icon = "HIDE_ON" row.prop(segment, 'glview', text="", toggle=True, icon=icon) row.prop(segment, 'gladvance', text="", toggle=True, icon="PREFERENCES") if segment.gltype == 20: # Area special split = row.split(factor=0.15, align=True) split.prop(segment, 'glcolorarea', text="") split = split.split(factor=0.20, align=True) split.prop(segment, 'glcolor', text="") else: split = row.split(factor=0.25, align=True) split.prop(segment, 'glcolor', text="") split.prop(segment, 'gltxt', text="") op = row.operator("measureit.deletesegment", text="", icon="X") op.tag = idx # saves internal data if segment.gladvance is True: row = box.row(align=True) row.prop(segment, 'glfont_size', text="Font") row.prop(segment, 'glfont_align', text="") if segment.glfont_align == 'L': row.prop(segment, 'glfont_rotat', text="Rotate") row = box.row(align=True) if segment.gltype != 9 and segment.gltype != 10 and segment.gltype != 20: row.prop(segment, 'glspace', text="Distance") row.prop(segment, 'glfontx', text="X") row.prop(segment, 'glfonty', text="Y") # Arrows if segment.gltype != 9 and segment.gltype != 10 and segment.gltype != 20: row = box.row(align=True) row.prop(segment, 'glarrow_a', text="") row.prop(segment, 'glarrow_b', text="") if segment.glarrow_a != '99' or segment.glarrow_b != '99': row.prop(segment, 'glarrow_s', text="Size") if segment.gltype != 2 and segment.gltype != 10: row = box.row(align=True) if scene.measureit_gl_show_d is True and segment.gltype != 9: row.prop(segment, 'gldist', text="Distance", toggle=True, icon="ALIGN_CENTER") if scene.measureit_gl_show_n is True: row.prop(segment, 'glnames', text="Text", toggle=True, icon="FONT_DATA") # sum distances if segment.gltype == 1 or segment.gltype == 12 or segment.gltype == 13 or segment.gltype == 14: row.prop(segment, 'gltot', text="Sum") if segment.gltype != 9 and segment.gltype != 10 and segment.gltype != 20: row = box.row(align=True) row.prop(segment, 'glwidth', text="Line") row.prop(segment, 'gldefault', text="Automatic position") if segment.gldefault is False: row = box.row(align=True) row.prop(segment, 'glnormalx', text="X") row.prop(segment, 'glnormaly', text="Y") row.prop(segment, 'glnormalz', text="Z") # Loc axis if segment.gltype != 2 and segment.gltype != 9 and segment.gltype != 10 \ and segment.gltype != 11 and segment.gltype != 12 and segment.gltype != 13 \ and segment.gltype != 14 and segment.gltype != 20: row = box.row(align=True) row.prop(segment, 'glocx', text="X", toggle=True) row.prop(segment, 'glocy', text="Y", toggle=True) row.prop(segment, 'glocz', text="Z", toggle=True) if segment.glocx is False or segment.glocy is False or segment.glocz is False: row = box.row() if segment.gltype == 1: row.prop(segment, 'glorto', text="Orthogonal") row.prop(segment, 'glocwarning', text="Warning") # ortogonal (only segments) if segment.gltype == 1: if segment.glorto != "99": row = box.row(align=True) row.prop(segment, 'glorto_x', text="X", toggle=True) row.prop(segment, 'glorto_y', text="Y", toggle=True) row.prop(segment, 'glorto_z', text="Z", toggle=True) # Arc special if segment.gltype == 11: row = box.row(align=True) row.prop(segment, 'glarc_rad', text="Radius") row.prop(segment, 'glarc_len', text="Length") row.prop(segment, 'glarc_ang', text="Angle") row = box.row(align=True) row.prop(segment, 'glarc_txradio', text="") row.prop(segment, 'glarc_txlen', text="") row.prop(segment, 'glarc_txang', text="") row = box.row(align=True) row.prop(segment, 'glarc_full', text="Full Circle") if segment.glarc_rad is True: row.prop(segment, 'glarc_extrad', text="Adapt radio") row = box.row(align=True) row.prop(segment, 'glarc_a', text="") row.prop(segment, 'glarc_b', text="") if segment.glarc_a != '99' or segment.glarc_b != '99': row.prop(segment, 'glarc_s', text="Size") # ------------------------------------------------------------------ # Define panel class for main functions. # ------------------------------------------------------------------ class MEASUREIT_PT_Main(Panel): bl_idname = "MEASUREIT_PT_Main" bl_label = "MeasureIt Tools" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category= 'View' bl_options = {'DEFAULT_CLOSED'} # ------------------------------ # Draw UI # ------------------------------ def draw(self, context): layout = self.layout scene = context.scene # ------------------------------ # Tool Buttons # ------------------------------ box = layout.box() # ------------------------------ # Display Buttons # ------------------------------ row = box.row() if context.window_manager.measureit_run_opengl is False: icon = 'PLAY' txt = 'Show' else: icon = "PAUSE" txt = 'Hide' row.operator("measureit.runopengl", text=txt, icon=icon) row.prop(scene, "measureit_gl_ghost", text="", icon='GHOST_ENABLED') # Tools box = layout.box() box.label(text="Add Measures") row = box.row() row.operator("measureit.addsegment", text="Segment") row.prop(scene, "measureit_sum", text="Sum") # To origin row = box.row() op = row.operator("measureit.addsegmentorto", text="X") op.tag = 0 # saves internal data op = row.operator("measureit.addsegmentorto", text="Y") op.tag = 1 # saves internal data op = row.operator("measureit.addsegmentorto", text="Z") op.tag = 2 # saves internal data row = box.row() row.operator("measureit.addangle", text="Angle", icon="LINCURVE") row.operator("measureit.addarc", text="Arc") row = box.row() row.operator("measureit.addlabel", text="Label", icon="FONT_DATA") row.operator("measureit.addnote", text="Annotation") row = box.row() row.operator("measureit.addlink", text="Link") row.operator("measureit.addorigin", text="Origin") row = box.row() row.operator("measureit.addarea", text="Area", icon="MESH_GRID") # ------------------------------ # Debug data # ------------------------------ box = layout.box() row = box.row(align=False) if scene.measureit_debug is False: row.prop(scene, "measureit_debug", icon="TRIA_RIGHT", text="Mesh Debug", emboss=False) else: row.prop(scene, "measureit_debug", icon="TRIA_DOWN", text="Mesh Debug", emboss=False) row = box.row() split = row.split(factor=0.10, align=True) split.prop(scene, 'measureit_debug_obj_color', text="") split.prop(scene, "measureit_debug_objects", icon="OBJECT_DATA") split.prop(scene, "measureit_debug_object_loc", icon="EMPTY_DATA") row = box.row() split = row.split(factor=0.10, align=True) split.prop(scene, 'measureit_debug_vert_color', text="") split.prop(scene, "measureit_debug_vertices", icon="VERTEXSEL") split.prop(scene, "measureit_debug_vert_loc", icon="EMPTY_DATA") if scene.measureit_debug_vert_loc is True: split.prop(scene, 'measureit_debug_vert_loc_toggle', text="") row = box.row() split = row.split(factor=0.10, align=True) split.prop(scene, 'measureit_debug_edge_color', text="") split = split.split(factor=0.5, align=True) split.prop(scene, "measureit_debug_edges", icon="EDGESEL") row = box.row() split = row.split(factor=0.10, align=True) split.prop(scene, 'measureit_debug_face_color', text="") split = split.split(factor=0.5, align=True) split.prop(scene, "measureit_debug_faces", icon="FACESEL") row = box.row() split = row.split(factor=0.10, align=True) split.prop(scene, 'measureit_debug_norm_color', text="") if scene.measureit_debug_normals is False: split = split.split(factor=0.50, align=True) split.prop(scene, "measureit_debug_normals", icon="OBJECT_ORIGIN") else: split = split.split(factor=0.5, align=True) split.prop(scene, "measureit_debug_normals", icon="OBJECT_ORIGIN") split.prop(scene, "measureit_debug_normal_size") row = box.row() split = row.split(factor=0.10, align=True) split.separator() split.prop(scene, "measureit_debug_normal_details") split.prop(scene, 'measureit_debug_width', text="Thickness") row = box.row(align=True) row.prop(scene, "measureit_debug_select", icon="GHOST_ENABLED") row.prop(scene, 'measureit_debug_font', text="Font") row.prop(scene, 'measureit_debug_precision', text="Precision") # ------------------------------------------------------------------ # Define panel class for conf functions. # ------------------------------------------------------------------ class MEASUREIT_PT_Conf(Panel): bl_idname = "MEASUREIT_PT_Conf" bl_label = "Configuration" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category= 'View' bl_parent_id = 'MEASUREIT_PT_Main' bl_options = {'DEFAULT_CLOSED'} # ------------------------------ # Draw UI # ------------------------------ def draw(self, context): layout = self.layout scene = context.scene # Configuration data box = layout.box() row = box.row() split = row.split(factor=0.2, align=True) split.label(text="Text") split = split.split(factor=0.2, align=True) split.prop(scene, "measureit_default_color", text="") split.prop(scene, "measureit_gl_txt", text="") row = box.row(align=True) row.prop(scene, "measureit_hint_space") row.prop(scene, "measureit_font_align", text="") # Arrow row = box.row(align=True) row.prop(scene, "measureit_glarrow_a", text="") row.prop(scene, "measureit_glarrow_b", text="") if scene.measureit_glarrow_a != '99' or scene.measureit_glarrow_b != '99': row.prop(scene, "measureit_glarrow_s", text="Size") row = box.row(align=True) row.prop(scene, "measureit_font_size") if scene.measureit_font_align == 'L': row.prop(scene, "measureit_font_rotation", text="Rotate") # ------------------------------------------------------------------ # Define panel class for render functions. # ------------------------------------------------------------------ class MEASUREIT_PT_Render(Panel): bl_idname = "MEASUREIT_PT_Render" bl_label = "Render" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category= 'Display' bl_parent_id = 'MEASUREIT_PT_Main' bl_options = {'DEFAULT_CLOSED'} # ------------------------------ # Draw UI # ------------------------------ def draw(self, context): layout = self.layout scene = context.scene # Render settings box = layout.box() row = box.row() row.prop(scene, "measureit_render_type") row = box.row() row.operator("measureit.rendersegment", icon='SCRIPT') row = box.row() row.prop(scene, "measureit_render", text="Save render image") row = box.row() row.prop(scene, "measureit_rf", text="Frame") if scene.measureit_rf is True: row.prop(scene, "measureit_rf_color", text="Color") row = box.row() row.prop(scene, "measureit_rf_border", text="Space") row.prop(scene, "measureit_rf_line", text="Width") # ------------------------------------------------------------- # Defines button that adds a measure segment # # ------------------------------------------------------------- class MEASUREIT_OT_AddSegment(Operator): bl_idname = "measureit.addsegment" bl_label = "Add" bl_description = "(EDITMODE only) Add a new measure segment between 2 vertices (select 2 vertices or more)" # ------------------------------ # Poll # ------------------------------ @classmethod def poll(cls, context): o = context.object if o is None: return False else: if o.type == "MESH": if bpy.context.mode == 'EDIT_MESH': return True else: return False else: return False # ------------------------------ # Execute button action # ------------------------------ def execute(self, context): if context.area.type == 'VIEW_3D': # Add properties scene = context.scene mainobject = context.object mylist = get_smart_selected(mainobject) if len(mylist) < 2: # if not selected linked vertex mylist = get_selected_vertex(mainobject) if len(mylist) >= 2: if 'MeasureGenerator' not in mainobject: mainobject.MeasureGenerator.add() mp = mainobject.MeasureGenerator[0] for x in range(0, len(mylist) - 1, 2): # ----------------------- # Only if not exist # ----------------------- if exist_segment(mp, mylist[x], mylist[x + 1]) is False: # Create all array elements for cont in range(len(mp.measureit_segments) - 1, mp.measureit_num): mp.measureit_segments.add() # Set values ms = mp.measureit_segments[mp.measureit_num] ms.gltype = 1 ms.glpointa = mylist[x] ms.glpointb = mylist[x + 1] ms.glarrow_a = scene.measureit_glarrow_a ms.glarrow_b = scene.measureit_glarrow_b ms.glarrow_s = scene.measureit_glarrow_s # color ms.glcolor = scene.measureit_default_color # dist ms.glspace = scene.measureit_hint_space # text ms.gltxt = scene.measureit_gl_txt ms.glfont_size = scene.measureit_font_size ms.glfont_align = scene.measureit_font_align ms.glfont_rotat = scene.measureit_font_rotation # Sum group ms.gltot = scene.measureit_sum # Add index mp.measureit_num += 1 # redraw context.area.tag_redraw() return {'FINISHED'} else: self.report({'ERROR'}, "MeasureIt: Select at least two vertices for creating measure segment.") return {'FINISHED'} else: self.report({'WARNING'}, "View3D not found, cannot run operator") return {'CANCELLED'} # ------------------------------------------------------------- # Defines button that adds an area measure # # ------------------------------------------------------------- class MEASUREIT_OT_AddArea(Operator): bl_idname = "measureit.addarea" bl_label = "Area" bl_description = "(EDITMODE only) Add a new measure for area (select 1 o more faces)" # ------------------------------ # Poll # ------------------------------ @classmethod def poll(cls, context): o = context.object if o is None: return False else: if o.type == "MESH": if bpy.context.mode == 'EDIT_MESH': return True else: return False else: return False # ------------------------------ # Execute button action # ------------------------------ def execute(self, context): if context.area.type == 'VIEW_3D': # Add properties scene = context.scene mainobject = context.object mylist = get_selected_faces(mainobject) if len(mylist) >= 1: if 'MeasureGenerator' not in mainobject: mainobject.MeasureGenerator.add() mp = mainobject.MeasureGenerator[0] mp.measureit_segments.add() ms = mp.measureit_segments[mp.measureit_num] ms.gltype = 20 f = -1 for face in mylist: # Create array elements ms.measureit_faces.add() f += 1 # Set values mf = ms.measureit_faces[f] mf.glface = f i = 0 for v in face: mf.measureit_index.add() mi = mf.measureit_index[i] mi.glidx = v i += 1 # color rgb = scene.measureit_default_color ms.glcolor = (rgb[0], rgb[1], rgb[2], 0.4) # dist ms.glspace = scene.measureit_hint_space # text ms.gltxt = scene.measureit_gl_txt ms.glfont_size = scene.measureit_font_size ms.glfont_align = scene.measureit_font_align ms.glfont_rotat = scene.measureit_font_rotation # Sum group ms.gltot = scene.measureit_sum # Add index mp.measureit_num += 1 # redraw context.area.tag_redraw() return {'FINISHED'} else: self.report({'ERROR'}, "MeasureIt: Select at least one face for creating area measure. ") return {'FINISHED'} else: self.report({'WARNING'}, "View3D not found, cannot run operator") return {'CANCELLED'} # ------------------------------------------------------------- # Defines button that adds a measure segment to x/y/z origin # # ------------------------------------------------------------- class MEASUREIT_OT_AddSegmentOrto(Operator): bl_idname = "measureit.addsegmentorto" bl_label = "Add" bl_description = "(EDITMODE only) Add a new measure segment from vertex to object origin for one " \ "axis (select 1 vertex)" tag: IntProperty() # ------------------------------ # Poll # ------------------------------ @classmethod def poll(cls, context): o = context.object if o is None: return False else: if o.type == "MESH": if bpy.context.mode == 'EDIT_MESH': return True else: return False else: return False # ------------------------------ # Execute button action # ------------------------------ def execute(self, context): if context.area.type == 'VIEW_3D': # Add properties scene = context.scene mainobject = context.object mylist = get_smart_selected(mainobject) if len(mylist) < 1: # if not selected linked vertex mylist = get_selected_vertex(mainobject) if len(mylist) >= 1: if 'MeasureGenerator' not in mainobject: mainobject.MeasureGenerator.add() mp = mainobject.MeasureGenerator[0] for x in range(len(mylist)): # ----------------------- # Only if not exist # ----------------------- if exist_segment(mp, mylist[x], mylist[x], 12 + int(self.tag)) is False: # Create all array elements for cont in range(len(mp.measureit_segments) - 1, mp.measureit_num): mp.measureit_segments.add() # Set values ms = mp.measureit_segments[mp.measureit_num] ms.gltype = 12 + int(self.tag) ms.glpointa = mylist[x] ms.glpointb = mylist[x] ms.glarrow_a = scene.measureit_glarrow_a ms.glarrow_b = scene.measureit_glarrow_b ms.glarrow_s = scene.measureit_glarrow_s # color ms.glcolor = scene.measureit_default_color # dist ms.glspace = scene.measureit_hint_space # text ms.gltxt = scene.measureit_gl_txt ms.glfont_size = scene.measureit_font_size ms.glfont_align = scene.measureit_font_align ms.glfont_rotat = scene.measureit_font_rotation # Sum group ms.gltot = scene.measureit_sum # Add index mp.measureit_num += 1 # redraw context.area.tag_redraw() return {'FINISHED'} else: self.report({'ERROR'}, "MeasureIt: Select at least one vertex for creating measure segment.") return {'FINISHED'} else: self.report({'WARNING'}, "View3D not found, cannot run operator") return {'CANCELLED'} # ------------------------------------------------------------- # Defines button that adds an angle measure # # ------------------------------------------------------------- class MEASUREIT_OT_AddAngle(Operator): bl_idname = "measureit.addangle" bl_label = "Angle" bl_description = "(EDITMODE only) Add a new angle measure (select 3 vertices, 2nd is angle vertex)" # ------------------------------ # Poll # ------------------------------ @classmethod def poll(cls, context): o = context.object if o is None: return False else: if o.type == "MESH": if bpy.context.mode == 'EDIT_MESH': return True else: return False else: return False # ------------------------------ # Execute button action # ------------------------------ def execute(self, context): if context.area.type == 'VIEW_3D': # Add properties scene = context.scene mainobject = context.object mylist = get_selected_vertex_history(mainobject) if len(mylist) == 3: if 'MeasureGenerator' not in mainobject: mainobject.MeasureGenerator.add() mp = mainobject.MeasureGenerator[0] # ----------------------- # Only if not exist # ----------------------- if exist_segment(mp, mylist[0], mylist[1], 9, mylist[2]) is False: # Create all array elements for cont in range(len(mp.measureit_segments) - 1, mp.measureit_num): mp.measureit_segments.add() # Set values ms = mp.measureit_segments[mp.measureit_num] ms.gltype = 9 ms.glpointa = mylist[0] ms.glpointb = mylist[1] ms.glpointc = mylist[2] # color ms.glcolor = scene.measureit_default_color # dist ms.glspace = scene.measureit_hint_space # text ms.gltxt = scene.measureit_gl_txt ms.glfont_size = scene.measureit_font_size ms.glfont_align = scene.measureit_font_align ms.glfont_rotat = scene.measureit_font_rotation # Add index mp.measureit_num += 1 # redraw context.area.tag_redraw() return {'FINISHED'} else: self.report({'ERROR'}, "MeasureIt: Select three vertices for creating angle measure") return {'FINISHED'} else: self.report({'WARNING'}, "View3D not found, cannot run operator") return {'CANCELLED'} # ------------------------------------------------------------- # Defines button that adds an arc measure # # ------------------------------------------------------------- class MEASUREIT_OT_AddArc(Operator): bl_idname = "measureit.addarc" bl_label = "Angle" bl_description = "(EDITMODE only) Add a new arc measure (select 3 vertices of the arc," \ " vertices 1st and 3rd are arc extremes)" # ------------------------------ # Poll # ------------------------------ @classmethod def poll(cls, context): o = context.object if o is None: return False else: if o.type == "MESH": if bpy.context.mode == 'EDIT_MESH': return True else: return False else: return False # ------------------------------ # Execute button action # ------------------------------ def execute(self, context): if context.area.type == 'VIEW_3D': # Add properties scene = context.scene mainobject = context.object mylist = get_selected_vertex_history(mainobject) if len(mylist) == 3: if 'MeasureGenerator' not in mainobject: mainobject.MeasureGenerator.add() mp = mainobject.MeasureGenerator[0] # ----------------------- # Only if not exist # ----------------------- if exist_segment(mp, mylist[0], mylist[1], 11, mylist[2]) is False: # Create all array elements for cont in range(len(mp.measureit_segments) - 1, mp.measureit_num): mp.measureit_segments.add() # Set values ms = mp.measureit_segments[mp.measureit_num] ms.gltype = 11 ms.glpointa = mylist[0] ms.glpointb = mylist[1] ms.glpointc = mylist[2] ms.glarrow_a = scene.measureit_glarrow_a ms.glarrow_b = scene.measureit_glarrow_b ms.glarrow_s = scene.measureit_glarrow_s # color ms.glcolor = scene.measureit_default_color # dist ms.glspace = scene.measureit_hint_space # text ms.gltxt = scene.measureit_gl_txt ms.glfont_size = scene.measureit_font_size ms.glfont_align = scene.measureit_font_align ms.glfont_rotat = scene.measureit_font_rotation # Add index mp.measureit_num += 1 # redraw context.area.tag_redraw() return {'FINISHED'} else: self.report({'ERROR'}, "MeasureIt: Select three vertices for creating arc measure") return {'FINISHED'} else: self.report({'WARNING'}, "View3D not found, cannot run operator") return {'CANCELLED'} # ------------------------------------------------------------- # Defines button that adds a label segment # # ------------------------------------------------------------- class MEASUREIT_OT_AddLabel(Operator): bl_idname = "measureit.addlabel" bl_label = "Add" bl_description = "(EDITMODE only) Add a new measure label (select 1 vertex)" # ------------------------------ # Poll # ------------------------------ @classmethod def poll(cls, context): o = context.object if o is None: return False else: if o.type == "MESH": if bpy.context.mode == 'EDIT_MESH': return True else: return False else: return False # ------------------------------ # Execute button action # ------------------------------ def execute(self, context): if context.area.type == 'VIEW_3D': # Add properties scene = context.scene mainobject = context.object mylist = get_selected_vertex(mainobject) if len(mylist) == 1: if 'MeasureGenerator' not in mainobject: mainobject.MeasureGenerator.add() mp = mainobject.MeasureGenerator[0] # ----------------------- # Only if not exist # ----------------------- if exist_segment(mp, mylist[0], mylist[0], 2) is False: # Both equal # Create all array elements for cont in range(len(mp.measureit_segments) - 1, mp.measureit_num): mp.measureit_segments.add() # Set values ms = mp.measureit_segments[mp.measureit_num] ms.gltype = 2 ms.glpointa = mylist[0] ms.glpointb = mylist[0] # Equal ms.glarrow_a = scene.measureit_glarrow_a ms.glarrow_b = scene.measureit_glarrow_b ms.glarrow_s = scene.measureit_glarrow_s # color ms.glcolor = scene.measureit_default_color # dist ms.glspace = scene.measureit_hint_space # text ms.gltxt = scene.measureit_gl_txt ms.glfont_size = scene.measureit_font_size ms.glfont_align = scene.measureit_font_align ms.glfont_rotat = scene.measureit_font_rotation # Add index mp.measureit_num += 1 # redraw context.area.tag_redraw() return {'FINISHED'} else: self.report({'ERROR'}, "MeasureIt: Select one vertex for creating measure label") return {'FINISHED'} else: self.report({'WARNING'}, "View3D not found, cannot run operator") return {'CANCELLED'} # ------------------------------------------------------------- # Defines button that adds a link # # ------------------------------------------------------------- class MEASUREIT_OT_AddLink(Operator): bl_idname = "measureit.addlink" bl_label = "Add" bl_description = "(OBJECT mode only) Add a new measure between objects (select 2 " \ "objects and optionally 1 or 2 vertices)" # ------------------------------ # Poll # ------------------------------ @classmethod def poll(cls, context): o = context.object if o is None: return False else: if o.type == "MESH" or o.type == "EMPTY" or o.type == "CAMERA" or o.type == "LIGHT": if bpy.context.mode == 'OBJECT': return True else: return False else: return False # ------------------------------ # Execute button action # ------------------------------ def execute(self, context): if context.area.type == 'VIEW_3D': scene = context.scene mainobject = context.object # ------------------------------- # Verify number of objects # ------------------------------- if len(context.selected_objects) != 2: self.report({'ERROR'}, "MeasureIt: Select two objects only, and optionally 1 vertex or 2 vertices " "(one of each object)") return {'FINISHED'} # Locate other object linkobject = None for o in context.selected_objects: if o.name != mainobject.name: linkobject = o.name # Verify destination vertex lkobj = bpy.data.objects[linkobject] mylinkvertex = get_selected_vertex(lkobj) if len(mylinkvertex) > 1: self.report({'ERROR'}, "MeasureIt: The destination object has more than one vertex selected. " "Select only 1 or none") return {'FINISHED'} # Verify origin vertex myobjvertex = get_selected_vertex(mainobject) if len(mylinkvertex) > 1: self.report({'ERROR'}, "MeasureIt: The active object has more than one vertex selected. Select only 1 or none") return {'FINISHED'} # ------------------------------- # Add properties # ------------------------------- flag = False if 'MeasureGenerator' not in mainobject: mainobject.MeasureGenerator.add() mp = mainobject.MeasureGenerator[0] # if exist_segment(mp, mylist[0], mylist[0], 3) is False: # flag = True # Create all array elements for cont in range(len(mp.measureit_segments) - 1, mp.measureit_num): mp.measureit_segments.add() # Set values ms = mp.measureit_segments[mp.measureit_num] # ----------------------- # Vertex to Vertex # ----------------------- if len(myobjvertex) == 1 and len(mylinkvertex) == 1: ms.gltype = 3 ms.glpointa = myobjvertex[0] ms.glpointb = mylinkvertex[0] flag = True # ----------------------- # Vertex to Object # ----------------------- if len(myobjvertex) == 1 and len(mylinkvertex) == 0: ms.gltype = 4 ms.glpointa = myobjvertex[0] ms.glpointb = 0 flag = True # ----------------------- # Object to Vertex # ----------------------- if len(myobjvertex) == 0 and len(mylinkvertex) == 1: ms.gltype = 5 ms.glpointa = 0 ms.glpointb = mylinkvertex[0] flag = True # ----------------------- # Object to Object # ----------------------- if len(myobjvertex) == 0 and len(mylinkvertex) == 0: ms.gltype = 8 ms.glpointa = 0 ms.glpointb = 0 # Equal flag = True # ------------------ # only if created # ------------------ if flag is True: ms.glarrow_a = scene.measureit_glarrow_a ms.glarrow_b = scene.measureit_glarrow_b ms.glarrow_s = scene.measureit_glarrow_s # color ms.glcolor = scene.measureit_default_color # dist ms.glspace = scene.measureit_hint_space # text ms.gltxt = scene.measureit_gl_txt ms.glfont_size = scene.measureit_font_size ms.glfont_align = scene.measureit_font_align ms.glfont_rotat = scene.measureit_font_rotation # link ms.gllink = linkobject # Add index mp.measureit_num += 1 # ----------------------- # Only if not exist # ----------------------- # redraw context.area.tag_redraw() return {'FINISHED'} else: self.report({'WARNING'}, "View3D not found, cannot run operator") return {'CANCELLED'} # ------------------------------------------------------------- # Defines button that adds an origin segment # # ------------------------------------------------------------- class MEASUREIT_OT_AddOrigin(Operator): bl_idname = "measureit.addorigin" bl_label = "Add" bl_description = "(OBJECT mode only) Add a new measure to origin (select object and optionally 1 vertex)" # ------------------------------ # Poll # ------------------------------ @classmethod def poll(cls, context): o = context.object if o is None: return False else: if o.type == "MESH" or o.type == "EMPTY" or o.type == "CAMERA" or o.type == "LIGHT": if bpy.context.mode == 'OBJECT': return True else: return False else: return False # ------------------------------ # Execute button action # ------------------------------ def execute(self, context): if context.area.type == 'VIEW_3D': # Add properties scene = context.scene mainobject = context.object mylist = get_selected_vertex(mainobject) if 'MeasureGenerator' not in mainobject: mainobject.MeasureGenerator.add() mp = mainobject.MeasureGenerator[0] # Create all array elements for cont in range(len(mp.measureit_segments) - 1, mp.measureit_num): mp.measureit_segments.add() # ----------------------- # Set values # ----------------------- ms = mp.measureit_segments[mp.measureit_num] flag = False if len(mylist) > 0: if len(mylist) == 1: if exist_segment(mp, mylist[0], mylist[0], 6) is False: # Both equal flag = True # Vertex to origin ms.gltype = 6 ms.glpointa = mylist[0] ms.glpointb = mylist[0] else: self.report({'ERROR'}, "MeasureIt: Enter in EDITMODE and select one vertex only for creating " "measure from vertex to origin") return {'FINISHED'} else: # Object to origin if exist_segment(mp, 0, 0, 7) is False: # Both equal flag = True ms.gltype = 7 ms.glpointa = 0 ms.glpointb = 0 # ------------------ # only if created # ------------------ if flag is True: ms.glarrow_a = scene.measureit_glarrow_a ms.glarrow_b = scene.measureit_glarrow_b ms.glarrow_s = scene.measureit_glarrow_s # color ms.glcolor = scene.measureit_default_color # dist ms.glspace = scene.measureit_hint_space # text ms.gltxt = scene.measureit_gl_txt ms.glfont_size = scene.measureit_font_size ms.glfont_align = scene.measureit_font_align ms.glfont_rotat = scene.measureit_font_rotation # Add index mp.measureit_num += 1 # redraw context.area.tag_redraw() return {'FINISHED'} else: self.report({'WARNING'}, "View3D not found, cannot run operator") return {'CANCELLED'} # ------------------------------------------------------------- # Defines button that deletes a measure segment # # ------------------------------------------------------------- class MEASUREIT_OT_DeleteSegment(Operator): bl_idname = "measureit.deletesegment" bl_label = "Delete" bl_description = "Delete a measure" tag: IntProperty() # ------------------------------ # Execute button action # ------------------------------ def execute(self, context): if context.area.type == 'VIEW_3D': # Add properties mainobject = context.object mp = mainobject.MeasureGenerator[0] ms = mp.measureit_segments[self.tag] ms.glfree = True # Delete element mp.measureit_segments.remove(self.tag) mp.measureit_num -= 1 # redraw context.area.tag_redraw() return {'FINISHED'} else: self.report({'WARNING'}, "View3D not found, cannot run operator") return {'CANCELLED'} # ------------------------------------------------------------- # Defines button that deletes all measure segments # # ------------------------------------------------------------- class MEASUREIT_OT_DeleteAllSegment(Operator): bl_idname = "measureit.deleteallsegment" bl_label = "Delete" bl_description = "Delete all measures (it cannot be undone)" tag: IntProperty() # ------------------------------ # Execute button action # ------------------------------ def execute(self, context): if context.area.type == 'VIEW_3D': # Add properties mainobject = context.object mp = mainobject.MeasureGenerator[0] while len(mp.measureit_segments) > 0: mp.measureit_segments.remove(0) # reset size mp.measureit_num = len(mp.measureit_segments) # redraw context.area.tag_redraw() return {'FINISHED'} else: self.report({'WARNING'}, "View3D not found, cannot run operator") return {'CANCELLED'} # ------------------------------------------------------------- # Defines button that deletes all measure segment sums # # ------------------------------------------------------------- class MEASUREIT_OT_DeleteAllSum(Operator): bl_idname = "measureit.deleteallsum" bl_label = "Delete" bl_description = "Delete all sum groups" tag: IntProperty() # ------------------------------ # Execute button action # ------------------------------ # noinspection PyMethodMayBeStatic def execute(self, context): if context.object is not None: if 'MeasureGenerator' in context.object: mp = context.object.MeasureGenerator[0] for idx in range(mp.measureit_num): ms = mp.measureit_segments[idx] ms.gltot = '99' return {'FINISHED'} # ------------------------------------------------------------- # Defines button that expands all measure segments # # ------------------------------------------------------------- class MEASUREIT_OT_ExpandAllSegment(Operator): bl_idname = "measureit.expandallsegment" bl_label = "Expand" bl_description = "Expand all measure properties" tag: IntProperty() # ------------------------------ # Execute button action # ------------------------------ def execute(self, context): if context.area.type == 'VIEW_3D': # Add properties mainobject = context.object mp = mainobject.MeasureGenerator[0] for i in mp.measureit_segments: i.gladvance = True return {'FINISHED'} else: self.report({'WARNING'}, "View3D not found, cannot run operator") return {'CANCELLED'} # ------------------------------------------------------------- # Defines button that collapses all measure segments # # ------------------------------------------------------------- class MEASUREIT_OT_CollapseAllSegment(Operator): bl_idname = "measureit.collapseallsegment" bl_label = "Collapse" bl_description = "Collapses all measure properties" tag: IntProperty() # ------------------------------ # Execute button action # ------------------------------ def execute(self, context): if context.area.type == 'VIEW_3D': # Add properties mainobject = context.object mp = mainobject.MeasureGenerator[0] for i in mp.measureit_segments: i.gladvance = False return {'FINISHED'} else: self.report({'WARNING'}, "View3D not found, cannot run operator") return {'CANCELLED'} # ------------------------------------------------------------- # Defines button for render option # # ------------------------------------------------------------- class MEASUREIT_OT_RenderSegment(Operator): bl_idname = "measureit.rendersegment" bl_label = "Render" bl_description = "Create a render image with measures. Use UV/Image editor to view image generated" tag: IntProperty() # ------------------------------ # Execute button action # ------------------------------ # noinspection PyMethodMayBeStatic,PyUnusedLocal def execute(self, context): scene = context.scene msg = "New image created with measures. Open it in UV/image editor" camera_msg = "Unable to render. No camera found" # ----------------------------- # Check camera # ----------------------------- if scene.camera is None: self.report({'ERROR'}, camera_msg) return {'FINISHED'} # ----------------------------- # Frame render # ----------------------------- if scene.measureit_render_type == "1": # noinspection PyBroadException if render_main(self, context) is True: self.report({'INFO'}, msg) # ----------------------------- # Animation # ----------------------------- if scene.measureit_render_type == "2": oldframe = scene.frame_current flag = False # loop frames for frm in range(scene.frame_start, scene.frame_end + 1): scene.frame_set(frm) print("MeasureIt: Rendering frame %04d" % frm) flag = render_main(self, context, True) if flag is False: break scene.frame_current = oldframe if flag is True: self.report({'INFO'}, msg) return {'FINISHED'} # --------------------- # Set cameraView # --------------------- # noinspection PyMethodMayBeStatic def set_camera_view(self): for area in bpy.context.screen.areas: if area.type == 'VIEW_3D': area.spaces[0].region_3d.view_perspective = 'CAMERA' # ------------------------------------- # Set only render status # ------------------------------------- # noinspection PyMethodMayBeStatic def set_only_render(self, status): screen = bpy.context.screen v3d = False s = None # get spaceview_3d in current screen for a in screen.areas: if a.type == 'VIEW_3D': for s in a.spaces: if s.type == 'VIEW_3D': v3d = s break if v3d is not False: s.show_only_render = status # ------------------------------------------------------------- # Defines a new note # # ------------------------------------------------------------- class MEASUREIT_OT_AddNote(Operator): bl_idname = "measureit.addnote" bl_label = "Note" bl_description = "(OBJECT mode only) Add a new annotation" tag: IntProperty() # ------------------------------ # Poll # ------------------------------ # noinspection PyUnusedLocal @classmethod def poll(cls, context): if bpy.context.mode == 'OBJECT': return True else: return False # ------------------------------ # Execute button action # ------------------------------ def execute(self, context): if context.area.type == 'VIEW_3D': bpy.ops.object.empty_add(type='PLAIN_AXES') myempty = bpy.data.objects[bpy.context.active_object.name] myempty.location = bpy.context.scene.cursor.location myempty.empty_display_size = 0.01 myempty.name = "Annotation" # Add properties scene = context.scene mainobject = myempty if 'MeasureGenerator' not in mainobject: mainobject.MeasureGenerator.add() mp = mainobject.MeasureGenerator[0] # Create all array elements for cont in range(len(mp.measureit_segments) - 1, mp.measureit_num): mp.measureit_segments.add() # Set values ms = mp.measureit_segments[mp.measureit_num] ms.gltype = 10 ms.glpointa = 0 ms.glpointb = 0 # Equal # color ms.glcolor = scene.measureit_default_color # dist ms.glspace = scene.measureit_hint_space # text ms.gltxt = scene.measureit_gl_txt ms.glfont_size = scene.measureit_font_size ms.glfont_align = scene.measureit_font_align ms.glfont_rotat = scene.measureit_font_rotation # Add index mp.measureit_num += 1 # redraw context.area.tag_redraw() return {'FINISHED'} else: self.report({'WARNING'}, "View3D not found, cannot run operator") return {'CANCELLED'} # ------------------------------------------------------------- # Defines button that enables/disables the tip display # # ------------------------------------------------------------- class MEASUREIT_OT_RunHintDisplay(Operator): bl_idname = "measureit.runopengl" bl_label = "Display hint data manager" bl_description = "Main control for enabling or disabling the display of measurements in the viewport" _handle = None # keep function handler # ---------------------------------- # Enable gl drawing adding handler # ---------------------------------- @staticmethod def handle_add(self, context): if MEASUREIT_OT_RunHintDisplay._handle is None: MEASUREIT_OT_RunHintDisplay._handle = SpaceView3D.draw_handler_add(draw_callback_px, (self, context), 'WINDOW', 'POST_PIXEL') context.window_manager.measureit_run_opengl = True # ------------------------------------ # Disable gl drawing removing handler # ------------------------------------ # noinspection PyUnusedLocal @staticmethod def handle_remove(self, context): if MEASUREIT_OT_RunHintDisplay._handle is not None: SpaceView3D.draw_handler_remove(MEASUREIT_OT_RunHintDisplay._handle, 'WINDOW') MEASUREIT_OT_RunHintDisplay._handle = None context.window_manager.measureit_run_opengl = False # ------------------------------ # Execute button action # ------------------------------ def execute(self, context): if context.area.type == 'VIEW_3D': if context.window_manager.measureit_run_opengl is False: self.handle_add(self, context) context.area.tag_redraw() else: self.handle_remove(self, context) context.area.tag_redraw() return {'FINISHED'} else: self.report({'WARNING'}, "View3D not found, cannot run operator") return {'CANCELLED'} # ------------------------------------------------------------- # Handle all draw routines (OpenGL main entry point) # # ------------------------------------------------------------- def draw_main(context): region = bpy.context.region # Detect if Quadview to get drawing area if not context.space_data.region_quadviews: rv3d = bpy.context.space_data.region_3d else: # verify area if context.area.type != 'VIEW_3D' or context.space_data.type != 'VIEW_3D': return i = -1 for region in context.area.regions: if region.type == 'WINDOW': i += 1 if context.region.id == region.id: break else: return rv3d = context.space_data.region_quadviews[i] scene = bpy.context.scene # Display selected or all if scene.measureit_gl_ghost is False: objlist = context.selected_objects else: objlist = context.view_layer.objects # Enable GL drawing bgl.glEnable(bgl.GL_BLEND) # --------------------------------------- # Generate all OpenGL calls for measures # --------------------------------------- for myobj in objlist: if myobj.visible_get() is True: if 'MeasureGenerator' in myobj: op = myobj.MeasureGenerator[0] draw_segments(context, myobj, op, region, rv3d) # --------------------------------------- # Generate all OpenGL calls for debug # --------------------------------------- if scene.measureit_debug is True: selobj = bpy.context.selected_objects for myobj in selobj: if scene.measureit_debug_objects is True: draw_object(context, myobj, region, rv3d) elif scene.measureit_debug_object_loc is True: draw_object(context, myobj, region, rv3d) if scene.measureit_debug_vertices is True: draw_vertices(context, myobj, region, rv3d) elif scene.measureit_debug_vert_loc is True: draw_vertices(context, myobj, region, rv3d) if scene.measureit_debug_edges is True: draw_edges(context, myobj, region, rv3d) if scene.measureit_debug_faces is True or scene.measureit_debug_normals is True: draw_faces(context, myobj, region, rv3d) # ----------------------- # restore opengl defaults # ----------------------- bgl.glDisable(bgl.GL_BLEND) # ------------------------------------------------------------- # Handler for drawing OpenGl # ------------------------------------------------------------- # noinspection PyUnusedLocal def draw_callback_px(self, context): draw_main(context) # ------------------------------------------------------------- # Check if the segment already exist # # ------------------------------------------------------------- def exist_segment(mp, pointa, pointb, typ=1, pointc=None): # for ms in mp.measureit_segments[mp.measureit_num] for ms in mp.measureit_segments: if ms.gltype == typ and ms.glfree is False: if typ != 9: if ms.glpointa == pointa and ms.glpointb == pointb: return True if ms.glpointa == pointb and ms.glpointb == pointa: return True else: if ms.glpointa == pointa and ms.glpointb == pointb and ms.glpointc == pointc: return True return False # ------------------------------------------------------------- # Get vertex selected # ------------------------------------------------------------- def get_selected_vertex(myobject): mylist = [] # if not mesh, no vertex if myobject.type != "MESH": return mylist # -------------------- # meshes # -------------------- oldobj = bpy.context.object bpy.context.view_layer.objects.active = myobject flag = False if myobject.mode != 'EDIT': bpy.ops.object.mode_set(mode='EDIT') flag = True bm = from_edit_mesh(myobject.data) tv = len(bm.verts) for v in bm.verts: if v.select: mylist.append(v.index) if flag is True: bpy.ops.object.editmode_toggle() # Back context object bpy.context.view_layer.objects.active = oldobj # if select all vertices, then use origin if tv == len(mylist): return [] return mylist # ------------------------------------------------------------- # Get vertex selected # ------------------------------------------------------------- def get_selected_vertex_history(myobject): mylist = [] # if not mesh, no vertex if myobject.type != "MESH": return mylist # -------------------- # meshes # -------------------- oldobj = bpy.context.object bpy.context.view_layer.objects.active = myobject flag = False if myobject.mode != 'EDIT': bpy.ops.object.mode_set(mode='EDIT') flag = True bm = from_edit_mesh(myobject.data) for v in bm.select_history: mylist.append(v.index) if flag is True: bpy.ops.object.editmode_toggle() # Back context object bpy.context.view_layer.objects.active = oldobj return mylist # ------------------------------------------------------------- # Get vertex selected segments # ------------------------------------------------------------- def get_smart_selected(myobject): mylist = [] # if not mesh, no vertex if myobject.type != "MESH": return mylist # -------------------- # meshes # -------------------- oldobj = bpy.context.object bpy.context.view_layer.objects.active = myobject flag = False if myobject.mode != 'EDIT': bpy.ops.object.mode_set(mode='EDIT') flag = True bm = from_edit_mesh(myobject.data) for e in bm.edges: if e.select is True: mylist.append(e.verts[0].index) mylist.append(e.verts[1].index) if flag is True: bpy.ops.object.editmode_toggle() # Back context object bpy.context.view_layer.objects.active = oldobj return mylist # ------------------------------------------------------------- # Get vertex selected faces # ------------------------------------------------------------- def get_selected_faces(myobject): mylist = [] # if not mesh, no vertex if myobject.type != "MESH": return mylist # -------------------- # meshes # -------------------- oldobj = bpy.context.object bpy.context.view_layer.objects.active = myobject flag = False if myobject.mode != 'EDIT': bpy.ops.object.mode_set(mode='EDIT') flag = True bm = from_edit_mesh(myobject.data) for e in bm.faces: myfaces = [] if e.select is True: for i in range(len(e.verts)): myfaces.append(e.verts[i].index) mylist.extend([myfaces]) if flag is True: bpy.ops.object.editmode_toggle() # Back context object bpy.context.view_layer.objects.active = oldobj return mylist