blender-addons/measureit/measureit_main.py
Campbell Barton e8da6131fd License headers: use SPDX-FileCopyrightText for all addons
Move copyright text to SPDX-FileCopyrightText or set to the
Blender Foundation so "make check_licenses" now runs without warnings.
2023-06-15 16:54:05 +10:00

2132 lines
82 KiB
Python

# SPDX-FileCopyrightText: 2016-2022 Blender Foundation
#
# 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
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 circumference",
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")
# orthogonal (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 drawing
gpu.state.blend_set('ALPHA')
# ---------------------------------------
# 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 defaults
# -----------------------
gpu.state.blend_set('NONE')
# -------------------------------------------------------------
# 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