blender-addons/measureit/measureit_geometry.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

1488 lines
59 KiB
Python

# SPDX-FileCopyrightText: 2016-2023 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# ----------------------------------------------------------
# support routines for OpenGL
# Author: Antonio Vazquez (antonioya)
#
# ----------------------------------------------------------
# noinspection PyUnresolvedReferences
import bpy
# noinspection PyUnresolvedReferences
import blf
from blf import ROTATION
from math import fabs, degrees, radians, sqrt, cos, sin, pi
from mathutils import Vector, Matrix
from bmesh import from_edit_mesh
from bpy_extras import view3d_utils, mesh_utils
import bpy_extras.object_utils as object_utils
from sys import exc_info
# GPU
import gpu
from gpu_extras.batch import batch_for_shader
shader = gpu.shader.from_builtin('UNIFORM_COLOR') if not bpy.app.background else None
shader_line = gpu.shader.from_builtin('POLYLINE_UNIFORM_COLOR') if not bpy.app.background else None
imm_line_width = 1.0
imm_viewport = (0, 0)
def imm_set_line_width(width):
global imm_line_width, imm_viewport
region = bpy.context.region
imm_viewport = (region.width, region.height)
imm_line_width = width
# -------------------------------------------------------------
# Draw segments
#
# -------------------------------------------------------------
# noinspection PyUnresolvedReferences,PyUnboundLocalVariable
def draw_segments(context, myobj, op, region, rv3d):
if op.measureit_num > 0:
a_code = "\u00b0" # degree
scale = bpy.context.scene.unit_settings.scale_length
scene = bpy.context.scene
pr = scene.measureit_gl_precision
fmt = "%1." + str(pr) + "f"
ovr = scene.measureit_ovr
ovrcolor = scene.measureit_ovr_color
ovrfsize = scene.measureit_ovr_font
ovrfang = get_angle_in_rad(scene.measureit_ovr_font_rotation)
ovrfaln = scene.measureit_ovr_font_align
ovrline = scene.measureit_ovr_width
units = scene.measureit_units
fang = get_angle_in_rad(scene.measureit_font_rotation)
# --------------------
# Scene Scale
# --------------------
if scene.measureit_scale is True:
prs = scene.measureit_scale_precision
fmts = "%1." + str(prs) + "f"
pos_2d = get_scale_txt_location(context)
tx_dsp = fmts % scene.measureit_scale_factor
tx_scale = scene.measureit_gl_scaletxt + " 1:" + tx_dsp
draw_text(myobj, pos_2d,
tx_scale, scene.measureit_scale_color, scene.measureit_scale_font,
text_rot=fang)
# --------------------
# Loop
# --------------------
for idx in range(op.measureit_num):
ms = op.measureit_segments[idx]
if ovr is False:
fsize = ms.glfont_size
fang = get_angle_in_rad(ms.glfont_rotat)
faln = ms.glfont_align
else:
fsize = ovrfsize
fang = ovrfang
faln = ovrfaln
# ------------------------------
# only active and visible
# ------------------------------
if ms.glview is True and ms.glfree is False:
# Arrow data
a_size = ms.glarrow_s
a_type = ms.glarrow_a
b_type = ms.glarrow_b
# noinspection PyBroadException
try:
if ovr is False:
rgba = ms.glcolor
else:
rgba = ovrcolor
# ----------------------
# Segment or Label
# ----------------------
if ms.gltype == 1 or ms.gltype == 2:
obverts = get_mesh_vertices(myobj)
if ms.glpointa <= len(obverts) and ms.glpointb <= len(obverts):
a_p1 = get_point(obverts[ms.glpointa].co, myobj)
b_p1 = get_point(obverts[ms.glpointb].co, myobj)
# ----------------------
# Segment or Label
# ----------------------
if ms.gltype == 12 or ms.gltype == 13 or ms.gltype == 14:
obverts = get_mesh_vertices(myobj)
if ms.glpointa <= len(obverts):
a_p1 = get_point(obverts[ms.glpointa].co, myobj)
if ms.gltype == 12: # X
b_p1 = get_point((0.0,
obverts[ms.glpointa].co[1],
obverts[ms.glpointa].co[2]), myobj)
elif ms.gltype == 13: # Y
b_p1 = get_point((obverts[ms.glpointa].co[0],
0.0,
obverts[ms.glpointa].co[2]), myobj)
else: # Z
b_p1 = get_point((obverts[ms.glpointa].co[0],
obverts[ms.glpointa].co[1],
0.0), myobj)
# ----------------------
# Vertex to Vertex (link)
# ----------------------
if ms.gltype == 3:
obverts = get_mesh_vertices(myobj)
linkverts = bpy.data.objects[ms.gllink].data.vertices
a_p1 = get_point(obverts[ms.glpointa].co, myobj)
b_p1 = get_point(linkverts[ms.glpointb].co, bpy.data.objects[ms.gllink])
# ----------------------
# Vertex to Object (link)
# ----------------------
if ms.gltype == 4:
obverts = get_mesh_vertices(myobj)
a_p1 = get_point(obverts[ms.glpointa].co, myobj)
b_p1 = get_location(bpy.data.objects[ms.gllink])
# ----------------------
# Object to Vertex (link)
# ----------------------
if ms.gltype == 5:
linkverts = bpy.data.objects[ms.gllink].data.vertices
a_p1 = get_location(myobj)
b_p1 = get_point(linkverts[ms.glpointb].co, bpy.data.objects[ms.gllink])
# ----------------------
# Object to Object (link)
# ----------------------
if ms.gltype == 8:
a_p1 = get_location(myobj)
b_p1 = get_location(bpy.data.objects[ms.gllink])
# ----------------------
# Vertex to origin
# ----------------------
if ms.gltype == 6:
obverts = get_mesh_vertices(myobj)
a_p1 = (0, 0, 0)
b_p1 = get_point(obverts[ms.glpointa].co, myobj)
# ----------------------
# Object to origin
# ----------------------
if ms.gltype == 7:
a_p1 = (0, 0, 0)
b_p1 = get_location(myobj)
# ----------------------
# Angle
# ----------------------
if ms.gltype == 9:
obverts = get_mesh_vertices(myobj)
if ms.glpointa <= len(obverts) and ms.glpointb <= len(obverts) and ms.glpointc <= len(obverts):
an_p1 = get_point(obverts[ms.glpointa].co, myobj)
an_p2 = get_point(obverts[ms.glpointb].co, myobj)
an_p3 = get_point(obverts[ms.glpointc].co, myobj)
ang_1 = Vector((an_p1[0] - an_p2[0], an_p1[1] - an_p2[1], an_p1[2] - an_p2[2]))
ang_2 = Vector((an_p3[0] - an_p2[0], an_p3[1] - an_p2[1], an_p3[2] - an_p2[2]))
ang_3 = ang_1 + ang_2 # Result vector
a_p1 = (an_p2[0], an_p2[1], an_p2[2])
b_p1 = (0, 0, 0)
# ----------------------
# Annotation
# ----------------------
if ms.gltype == 10:
a_p1 = get_location(myobj)
b_p1 = get_location(myobj)
# ----------------------
# Arc
# ----------------------
if ms.gltype == 11:
obverts = get_mesh_vertices(myobj)
if ms.glpointa <= len(obverts) and ms.glpointb <= len(obverts) and ms.glpointc <= len(obverts):
an_p1 = get_point(obverts[ms.glpointa].co, myobj)
an_p2 = get_point(obverts[ms.glpointb].co, myobj)
an_p3 = get_point(obverts[ms.glpointc].co, myobj)
# reference for maths: http://en.wikipedia.org/wiki/Circumscribed_circle
an_p12 = Vector((an_p1[0] - an_p2[0], an_p1[1] - an_p2[1], an_p1[2] - an_p2[2]))
an_p13 = Vector((an_p1[0] - an_p3[0], an_p1[1] - an_p3[1], an_p1[2] - an_p3[2]))
an_p21 = Vector((an_p2[0] - an_p1[0], an_p2[1] - an_p1[1], an_p2[2] - an_p1[2]))
an_p23 = Vector((an_p2[0] - an_p3[0], an_p2[1] - an_p3[1], an_p2[2] - an_p3[2]))
an_p31 = Vector((an_p3[0] - an_p1[0], an_p3[1] - an_p1[1], an_p3[2] - an_p1[2]))
an_p32 = Vector((an_p3[0] - an_p2[0], an_p3[1] - an_p2[1], an_p3[2] - an_p2[2]))
an_p12xp23 = an_p12.copy().cross(an_p23)
# radius = an_p12.length * an_p23.length * an_p31.length / (2 * an_p12xp23.length)
alpha = pow(an_p23.length, 2) * an_p12.dot(an_p13) / (2 * pow(an_p12xp23.length, 2))
beta = pow(an_p13.length, 2) * an_p21.dot(an_p23) / (2 * pow(an_p12xp23.length, 2))
gamma = pow(an_p12.length, 2) * an_p31.dot(an_p32) / (2 * pow(an_p12xp23.length, 2))
a_p1 = (alpha * an_p1[0] + beta * an_p2[0] + gamma * an_p3[0],
alpha * an_p1[1] + beta * an_p2[1] + gamma * an_p3[1],
alpha * an_p1[2] + beta * an_p2[2] + gamma * an_p3[2])
b_p1 = (an_p2[0], an_p2[1], an_p2[2])
a_n = an_p12.cross(an_p23)
a_n.normalize() # normal vector
arc_angle, arc_length = get_arc_data(an_p1, a_p1, an_p2, an_p3)
# Apply scale to arc_length
arc_length *= scene.measureit_scale_factor
# ----------------------
# Area
# ----------------------
if ms.gltype == 20:
a_p1 = get_location(myobj) # Not used
b_p1 = get_location(myobj) # Not used
# Calculate distance
dist, distloc = distance(a_p1, b_p1, ms.glocx, ms.glocy, ms.glocz)
# ------------------------------------
# get normal vector
# ------------------------------------
if ms.gldefault is True:
if ms.gltype == 9:
vn = ang_3 # if angle, vector is angle position
elif ms.gltype == 11:
vn = a_n # if arc, vector is perpendicular to surface of the three vertices
else:
loc = get_location(myobj)
midpoint3d = interpolate3d(a_p1, b_p1, fabs(dist / 2))
vn = Vector((midpoint3d[0] - loc[0],
midpoint3d[1] - loc[1],
midpoint3d[2] - loc[2]))
else:
vn = Vector((ms.glnormalx, ms.glnormaly, ms.glnormalz))
vn.normalize()
# ------------------------------------
# position vector
# ------------------------------------
vi = vn * ms.glspace
s = (14 / 200)
if s > ms.glspace:
s = ms.glspace / 5
vi2 = vn * (ms.glspace + s)
# ------------------------------------
# apply vector
# ------------------------------------
v1 = [a_p1[0] + vi[0], a_p1[1] + vi[1], a_p1[2] + vi[2]]
v2 = [b_p1[0] + vi[0], b_p1[1] + vi[1], b_p1[2] + vi[2]]
# Segment extreme
v11 = [a_p1[0] + vi2[0], a_p1[1] + vi2[1], a_p1[2] + vi2[2]]
v22 = [b_p1[0] + vi2[0], b_p1[1] + vi2[1], b_p1[2] + vi2[2]]
# Labeling
v11a = (a_p1[0] + vi2[0], a_p1[1] + vi2[1], a_p1[2] + vi2[2] + s / 30)
v11b = (a_p1[0] + vi2[0], a_p1[1] + vi2[1], a_p1[2] + vi2[2] - s / 40)
# Annotation
vn1 = (a_p1[0], a_p1[1], a_p1[2])
# -------------------------------------------
# Orthogonal
# -------------------------------------------
if ms.gltype == 1 and ms.glorto != "99":
if ms.glorto == "0": # A
if ms.glorto_x is True:
v1[0] = v2[0]
v11[0] = v22[0]
if ms.glorto_y is True:
v1[1] = v2[1]
v11[1] = v22[1]
if ms.glorto_z is True:
v1[2] = v2[2]
v11[2] = v22[2]
if ms.glorto == "1": # B
if ms.glorto_x is True:
v2[0] = v1[0]
v22[0] = v11[0]
if ms.glorto_y is True:
v2[1] = v1[1]
v22[1] = v11[1]
if ms.glorto_z is True:
v2[2] = v1[2]
v22[2] = v11[2]
# ------------------------------------
# converting to screen coordinates
# ------------------------------------
screen_point_ap1 = get_2d_point(region, rv3d, a_p1)
screen_point_bp1 = get_2d_point(region, rv3d, b_p1)
screen_point_v1 = get_2d_point(region, rv3d, v1)
screen_point_v2 = get_2d_point(region, rv3d, v2)
screen_point_v11 = get_2d_point(region, rv3d, v11)
screen_point_v22 = get_2d_point(region, rv3d, v22)
screen_point_v11a = get_2d_point(region, rv3d, v11a)
screen_point_v11b = get_2d_point(region, rv3d, v11b)
# ------------------------------------
# colour + line setup
# ------------------------------------
if ovr is False:
imm_set_line_width(ms.glwidth)
else:
imm_set_line_width(ovrline)
# ------------------------------------
# Text (distance)
# ------------------------------------
# noinspection PyBroadException
if ms.gltype != 2 and ms.gltype != 9 and ms.gltype != 10 and ms.gltype != 11 and ms.gltype != 20:
# noinspection PyBroadException
try:
midpoint3d = interpolate3d(v1, v2, fabs(dist / 2))
gap3d = (midpoint3d[0], midpoint3d[1], midpoint3d[2] + s / 2)
tmp_point = get_2d_point(region, rv3d, gap3d)
if tmp_point is None:
pass
txtpoint2d = tmp_point[0] + ms.glfontx, tmp_point[1] + ms.glfonty
# Scale
if scene.measureit_scale is True:
dist = dist * scene.measureit_scale_factor
distloc = distloc * scene.measureit_scale_factor
# decide dist to use
if dist == distloc:
locflag = False
usedist = dist
else:
usedist = distloc
locflag = True
# Apply scene scale
usedist *= scale
tx_dist = str(format_distance(fmt, units, usedist))
# -----------------------------------
# Draw text
# -----------------------------------
if scene.measureit_gl_show_d is True and ms.gldist is True:
msg = tx_dist + " "
else:
msg = " "
if scene.measureit_gl_show_n is True and ms.glnames is True:
msg += ms.gltxt
if scene.measureit_gl_show_d is True or scene.measureit_gl_show_n is True:
draw_text(myobj, txtpoint2d, msg, rgba, fsize, faln, fang)
# ------------------------------
# if axis loc, show a indicator
# ------------------------------
if locflag is True and ms.glocwarning is True:
txtpoint2d = get_2d_point(region, rv3d, (v2[0], v2[1], v2[2]))
txt = "["
if ms.glocx is True:
txt += "X"
if ms.glocy is True:
txt += "Y"
if ms.glocz is True:
txt += "Z"
txt += "]"
draw_text(myobj, txtpoint2d, txt, rgba, fsize - 1, text_rot=fang)
except:
pass
# ------------------------------------
# Text (label) and Angles
# ------------------------------------
# noinspection PyBroadException
if ms.gltype == 2 or ms.gltype == 9 or ms.gltype == 11:
tx_dist = ""
# noinspection PyBroadException
try:
if ms.gltype == 2:
tx_dist = ms.gltxt
if ms.gltype == 9: # Angles
ang = ang_1.angle(ang_2)
if bpy.context.scene.unit_settings.system_rotation == "DEGREES":
ang = degrees(ang)
tx_dist = " " + fmt % ang
# Add degree symbol
if bpy.context.scene.unit_settings.system_rotation == "DEGREES":
tx_dist += a_code
if scene.measureit_gl_show_n is True:
tx_dist += " " + ms.gltxt
if ms.gltype == 11: # arc
# print length or arc and angle
if ms.glarc_len is True:
tx_dist = ms.glarc_txlen + format_distance(fmt, units, arc_length * scale)
else:
tx_dist = " "
if bpy.context.scene.unit_settings.system_rotation == "DEGREES":
arc_d = degrees(arc_angle)
else:
arc_d = arc_angle
if ms.glarc_ang is True:
tx_dist += " " + ms.glarc_txang + format_distance(fmt, 9, arc_d)
# Add degree symbol
if bpy.context.scene.unit_settings.system_rotation == "DEGREES":
tx_dist += a_code
if scene.measureit_gl_show_d is True and ms.gldist is True:
msg = tx_dist + " "
else:
msg = " "
if scene.measureit_gl_show_n is True and ms.glnames is True:
msg += ms.gltxt
if scene.measureit_gl_show_d is True or scene.measureit_gl_show_n is True:
# Normal vector
vna = Vector((b_p1[0] - a_p1[0],
b_p1[1] - a_p1[1],
b_p1[2] - a_p1[2]))
vna.normalize()
via = vna * ms.glspace
gap3d = (b_p1[0] + via[0], b_p1[1] + via[1], b_p1[2] + via[2])
tmp_point = get_2d_point(region, rv3d, gap3d)
if tmp_point is not None:
txtpoint2d = tmp_point[0] + ms.glfontx, tmp_point[1] + ms.glfonty
draw_text(myobj, txtpoint2d, msg, rgba, fsize, faln, fang)
# Radius
if scene.measureit_gl_show_d is True and ms.gldist is True and \
ms.glarc_rad is True:
tx_dist = ms.glarc_txradio + format_distance(fmt, units,
dist * scene.measureit_scale_factor * scale)
else:
tx_dist = " "
if ms.gltype == 2:
gap3d = (v11a[0], v11a[1], v11a[2])
else:
gap3d = (a_p1[0], a_p1[1], a_p1[2])
tmp_point = get_2d_point(region, rv3d, gap3d)
if tmp_point is not None:
txtpoint2d = tmp_point[0] + ms.glfontx, tmp_point[1] + ms.glfonty
draw_text(myobj, txtpoint2d, tx_dist, rgba, fsize, faln, fang)
except:
pass
# ------------------------------------
# Annotation
# ------------------------------------
# noinspection PyBroadException
if ms.gltype == 10:
# noinspection PyBroadException
tx_dist = ms.gltxt
gap3d = (vn1[0], vn1[1], vn1[2])
tmp_point = get_2d_point(region, rv3d, gap3d)
if tmp_point is not None:
txtpoint2d = tmp_point[0] + ms.glfontx, tmp_point[1] + ms.glfonty
draw_text(myobj, txtpoint2d, tx_dist, rgba, fsize, faln, fang)
# ------------------------------------
# Draw lines
# ------------------------------------
gpu.state.blend_set('ALPHA')
if ms.gltype == 1: # Segment
draw_line(screen_point_ap1, screen_point_v11, rgba)
draw_line(screen_point_bp1, screen_point_v22, rgba)
draw_arrow(screen_point_v1, screen_point_v2, rgba, a_size, a_type, b_type)
if ms.gltype == 12 or ms.gltype == 13 or ms.gltype == 14: # Segment to origin
draw_line(screen_point_ap1, screen_point_v11, rgba)
draw_line(screen_point_bp1, screen_point_v22, rgba)
draw_arrow(screen_point_v1, screen_point_v2, rgba, a_size, a_type, b_type)
if ms.gltype == 2: # Label
draw_line(screen_point_v11a, screen_point_v11b, rgba)
draw_arrow(screen_point_ap1, screen_point_v11, rgba, a_size, a_type, b_type)
if ms.gltype == 3 or ms.gltype == 4 or ms.gltype == 5 or ms.gltype == 8 \
or ms.gltype == 6 or ms.gltype == 7: # Origin and Links
draw_arrow(screen_point_ap1, screen_point_bp1, rgba, a_size, a_type, b_type)
if ms.gltype == 9: # Angle
dist, distloc = distance(an_p1, an_p2)
mp1 = interpolate3d(an_p1, an_p2, fabs(dist / 1.1))
dist, distloc = distance(an_p3, an_p2)
mp2 = interpolate3d(an_p3, an_p2, fabs(dist / 1.1))
screen_point_an_p1 = get_2d_point(region, rv3d, mp1)
screen_point_an_p2 = get_2d_point(region, rv3d, an_p2)
screen_point_an_p3 = get_2d_point(region, rv3d, mp2)
draw_line(screen_point_an_p1, screen_point_an_p2, rgba)
draw_line(screen_point_an_p2, screen_point_an_p3, rgba)
draw_line(screen_point_an_p1, screen_point_an_p3, rgba)
if ms.gltype == 11: # arc
# draw line from center of arc second point
c = Vector(a_p1)
if ms.glarc_rad is True:
if ms.glarc_extrad is False:
draw_arrow(screen_point_ap1, screen_point_bp1, rgba, a_size, a_type, b_type)
else:
vne = Vector((b_p1[0] - a_p1[0],
b_p1[1] - a_p1[1],
b_p1[2] - a_p1[2]))
vne.normalize()
vie = vne * ms.glspace
pe = (b_p1[0] + vie[0], b_p1[1] + vie[1], b_p1[2] + vie[2])
screen_point_pe = get_2d_point(region, rv3d, pe)
draw_arrow(screen_point_ap1, screen_point_pe, rgba, a_size, a_type, b_type)
# create arc around the centerpoint
# rotation matrix around normal vector at center point
mat_trans1 = Matrix.Translation(-c)
# get step
n_step = 36.0
if ms.glarc_full is False:
step = arc_angle / n_step
else:
step = radians(360.0) / n_step
mat_rot1 = Matrix.Rotation(step, 4, vn)
mat_trans2 = Matrix.Translation(c)
p1 = Vector(an_p1) # first point of arc
# Normal vector
vn = Vector((p1[0] - a_p1[0],
p1[1] - a_p1[1],
p1[2] - a_p1[2]))
vn.normalize()
vi = vn * ms.glspace
p_01a = None
p_01b = None
p_02a = None
p_02b = None
# draw the arc
for i in range(int(n_step)):
p2 = mat_trans2 @ mat_rot1 @ mat_trans1 @ p1
p1_ = (p1[0] + vi[0], p1[1] + vi[1], p1[2] + vi[2])
# First Point
if i == 0:
p_01a = (p1_[0], p1_[1], p1_[2])
p_01b = (p1[0], p1[1], p1[2])
# Normal vector
vn = Vector((p2[0] - a_p1[0],
p2[1] - a_p1[1],
p2[2] - a_p1[2]))
vn.normalize()
vi = vn * ms.glspace
p2_ = (p2[0] + vi[0], p2[1] + vi[1], p2[2] + vi[2])
# convert to screen coordinates
screen_point_p1 = get_2d_point(region, rv3d, p1_)
screen_point_p2 = get_2d_point(region, rv3d, p2_)
if i == 0:
draw_arrow(screen_point_p1, screen_point_p2, rgba, ms.glarc_s, ms.glarc_a, "99")
elif i == int(n_step) - 1:
draw_arrow(screen_point_p1, screen_point_p2, rgba, ms.glarc_s, "99", ms.glarc_b)
else:
draw_line(screen_point_p1, screen_point_p2, rgba)
p1 = p2.copy()
# Last Point
if i == int(n_step) - 1:
p_02a = (p2_[0], p2_[1], p2_[2])
p_02b = (p2[0], p2[1], p2[2])
# Draw close lines
if ms.glarc_full is False:
screen_point_p1a = get_2d_point(region, rv3d, p_01a)
screen_point_p1b = get_2d_point(region, rv3d, p_01b)
screen_point_p2a = get_2d_point(region, rv3d, p_02a)
screen_point_p2b = get_2d_point(region, rv3d, p_02b)
draw_line(screen_point_p1a, screen_point_p1b, rgba)
draw_line(screen_point_p2a, screen_point_p2b, rgba)
if ms.gltype == 20: # Area
obverts = get_mesh_vertices(myobj)
tot = 0
if scene.measureit_scale is True:
ms_scale = scene.measureit_scale_factor
else:
ms_scale = 1.0
for face in ms.measureit_faces:
myvertices = []
for v in face.measureit_index:
myvertices.append(v.glidx)
area = get_area_and_paint(myvertices, myobj, obverts, region, rv3d, rgba, ms_scale)
tot += area
# Draw Area number over first face
if len(ms.measureit_faces) > 0:
face = ms.measureit_faces[0]
a = face.measureit_index[0].glidx
b = face.measureit_index[2].glidx
p1 = get_point(obverts[a].co, myobj)
p2 = get_point(obverts[b].co, myobj)
d1, dn = distance(p1, p2)
midpoint3d = interpolate3d(p1, p2, fabs(d1 / 2))
# mult by world scale
tot *= scale
tx_dist = str(format_distance(fmt, units, tot, 2))
# -----------------------------------
# Draw text
# -----------------------------------
if scene.measureit_gl_show_d is True and ms.gldist is True:
msg = tx_dist + " "
else:
msg = " "
if scene.measureit_gl_show_n is True and ms.glnames is True:
msg += ms.gltxt
if scene.measureit_gl_show_d is True or scene.measureit_gl_show_n is True:
tmp_point = get_2d_point(region, rv3d, midpoint3d)
if tmp_point is not None:
txtpoint2d = tmp_point[0] + ms.glfontx, tmp_point[1] + ms.glfonty
# todo: swap ms.glcolorarea with ms.glcolor ?
draw_text(myobj, txtpoint2d, msg, ms.glcolorarea, fsize, faln, fang)
except IndexError:
ms.glfree = True
except:
print("Unexpected error:" + str(exc_info()))
pass
return
# ------------------------------------------
# Get polygon area and paint area
#
# ------------------------------------------
def get_area_and_paint(myvertices, myobj, obverts, region, rv3d, rgba, ms_scale):
mymesh = myobj.data
totarea = 0
if len(myvertices) > 3:
# Tessellate the polygon
if myobj.mode != 'EDIT':
tris = mesh_utils.ngon_tessellate(mymesh, myvertices)
else:
bm = from_edit_mesh(myobj.data)
myv = []
for v in bm.verts:
myv.append(v.co)
tris = mesh_utils.ngon_tessellate(myv, myvertices)
for t in tris:
v1, v2, v3 = t
p1 = get_point(obverts[myvertices[v1]].co, myobj)
p2 = get_point(obverts[myvertices[v2]].co, myobj)
p3 = get_point(obverts[myvertices[v3]].co, myobj)
screen_point_p1 = get_2d_point(region, rv3d, p1)
screen_point_p2 = get_2d_point(region, rv3d, p2)
screen_point_p3 = get_2d_point(region, rv3d, p3)
draw_triangle(screen_point_p1, screen_point_p2, screen_point_p3, rgba)
# Area
area = get_triangle_area(p1, p2, p3, ms_scale)
totarea += area
elif len(myvertices) == 3:
v1, v2, v3 = myvertices
p1 = get_point(obverts[v1].co, myobj)
p2 = get_point(obverts[v2].co, myobj)
p3 = get_point(obverts[v3].co, myobj)
screen_point_p1 = get_2d_point(region, rv3d, p1)
screen_point_p2 = get_2d_point(region, rv3d, p2)
screen_point_p3 = get_2d_point(region, rv3d, p3)
draw_triangle(screen_point_p1, screen_point_p2, screen_point_p3, rgba)
# Area
area = get_triangle_area(p1, p2, p3, ms_scale)
totarea += area
else:
return 0.0
# Apply world scale
totarea *= bpy.context.scene.unit_settings.scale_length
return totarea
# ------------------------------------------
# Get area using Heron formula
#
# ------------------------------------------
def get_triangle_area(p1, p2, p3, scale=1.0):
d1, dn = distance(p1, p2)
d2, dn = distance(p2, p3)
d3, dn = distance(p1, p3)
d1 *= scale
d2 *= scale
d3 *= scale
per = (d1 + d2 + d3) / 2.0
area = sqrt(per * (per - d1) * (per - d2) * (per - d3))
return area
# ------------------------------------------
# Get point in 2d space
#
# ------------------------------------------
def get_2d_point(region, rv3d, point3d):
if rv3d is not None and region is not None:
return view3d_utils.location_3d_to_region_2d(region, rv3d, point3d)
else:
return get_render_location(point3d)
# -------------------------------------------------------------
# Get sum of a group
#
# myobj: Current object
# Tag: group
# -------------------------------------------------------------
def get_group_sum(myobj, tag):
# noinspection PyBroadException
try:
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"]
g = tag[2:3]
mp = myobj.MeasureGenerator[0]
flag = False
# -----------------
# Sum loop segments
# -----------------
scale = bpy.context.scene.unit_settings.scale_length
tot = 0.0
obverts = get_mesh_vertices(myobj)
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 and g == tx[int(ms.gltot)]: # only segments
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 += usedist
flag = True
if flag is True:
# Return value
pr = bpy.context.scene.measureit_gl_precision
fmt = "%1." + str(pr) + "f"
units = bpy.context.scene.measureit_units
return format_distance(fmt, units, tot)
else:
return " "
except:
return " "
# -------------------------------------------------------------
# Create OpenGL text
#
# -------------------------------------------------------------
def draw_text(myobj, pos2d, display_text, rgba, fsize, align='L', text_rot=0.0):
if pos2d is None:
return
# dpi = bpy.context.preferences.system.dpi
gap = 12
x_pos, y_pos = pos2d
font_id = 0
ui_scale = bpy.context.preferences.system.ui_scale
blf.size(font_id, round(fsize * ui_scale))
# blf.size(font_id, fsize)
# height of one line
mwidth, mheight = blf.dimensions(font_id, "Tp") # uses high/low letters
# Calculate sum groups
m = 0
while "<#" in display_text:
m += 1
if m > 10: # limit loop
break
i = display_text.index("<#")
tag = display_text[i:i + 4]
display_text = display_text.replace(tag, get_group_sum(myobj, tag.upper()))
# split lines
mylines = display_text.split("|")
idx = len(mylines) - 1
maxwidth = 0
maxheight = len(mylines) * mheight
# -------------------
# Draw all lines
# -------------------
for line in mylines:
text_width, text_height = blf.dimensions(font_id, line)
if align == 'C':
newx = x_pos - text_width / 2
elif align == 'R':
newx = x_pos - text_width - gap
else:
newx = x_pos
blf.enable(font_id, ROTATION)
blf.rotation(font_id, text_rot)
# calculate new Y position
new_y = y_pos + (mheight * idx)
# Draw
blf.position(font_id, newx, new_y, 0)
blf.color(font_id, rgba[0], rgba[1], rgba[2], rgba[3])
blf.draw(font_id, " " + line)
# sub line
idx -= 1
# saves max width
if maxwidth < text_width:
maxwidth = text_width
if align == 'L':
blf.disable(font_id, ROTATION)
return maxwidth, maxheight
# -------------------------------------------------------------
# Draw an OpenGL line
#
# -------------------------------------------------------------
def draw_line(v1, v2, rgba):
coords = [(v1[0], v1[1], 0), (v2[0], v2[1], 0)]
batch = batch_for_shader(shader_line, 'LINES', {"pos": coords})
# noinspection PyBroadException
try:
if v1 is not None and v2 is not None:
shader_line.bind()
shader_line.uniform_float("color", rgba)
shader_line.uniform_float("lineWidth", imm_line_width)
shader_line.uniform_float("viewportSize", imm_viewport)
batch.draw(shader_line)
except:
pass
# -------------------------------------------------------------
# Draw an OpenGL triangle
#
# -------------------------------------------------------------
def draw_triangle(v1, v2, v3, rgba):
coords = [(v1[0], v1[1]), (v2[0], v2[1]), (v3[0], v3[1])]
batch = batch_for_shader(shader, 'TRIS', {"pos": coords})
# noinspection PyBroadException
try:
if v1 is not None and v2 is not None and v3 is not None:
shader.bind()
shader.uniform_float("color", rgba)
batch.draw(shader)
except:
pass
# -------------------------------------------------------------
# Draw an Arrow
#
# -------------------------------------------------------------
def draw_arrow(v1, v2, rgba, size=20, a_typ="1", b_typ="1"):
if v1 is None or v2 is None:
return
rad45 = radians(45)
rad315 = radians(315)
rad90 = radians(90)
rad270 = radians(270)
v = interpolate3d((v1[0], v1[1], 0.0), (v2[0], v2[1], 0.0), size)
v1i = (v[0] - v1[0], v[1] - v1[1])
v = interpolate3d((v2[0], v2[1], 0.0), (v1[0], v1[1], 0.0), size)
v2i = (v[0] - v2[0], v[1] - v2[1])
# Point A
if a_typ == "3":
rad_a = rad90
rad_b = rad270
else:
rad_a = rad45
rad_b = rad315
v1a = (int(v1i[0] * cos(rad_a) - v1i[1] * sin(rad_a) + v1[0]),
int(v1i[1] * cos(rad_a) + v1i[0] * sin(rad_a)) + v1[1])
v1b = (int(v1i[0] * cos(rad_b) - v1i[1] * sin(rad_b) + v1[0]),
int(v1i[1] * cos(rad_b) + v1i[0] * sin(rad_b) + v1[1]))
# Point B
if b_typ == "3":
rad_a = rad90
rad_b = rad270
else:
rad_a = rad45
rad_b = rad315
v2a = (int(v2i[0] * cos(rad_a) - v2i[1] * sin(rad_a) + v2[0]),
int(v2i[1] * cos(rad_a) + v2i[0] * sin(rad_a)) + v2[1])
v2b = (int(v2i[0] * cos(rad_b) - v2i[1] * sin(rad_b) + v2[0]),
int(v2i[1] * cos(rad_b) + v2i[0] * sin(rad_b) + v2[1]))
# Triangle o Lines
if a_typ == "1" or a_typ == "3":
draw_line(v1, v1a, rgba)
draw_line(v1, v1b, rgba)
if b_typ == "1" or b_typ == "3":
draw_line(v2, v2a, rgba)
draw_line(v2, v2b, rgba)
if a_typ == "2":
draw_triangle(v1, v1a, v1b, rgba)
if b_typ == "2":
draw_triangle(v2, v2a, v2b, rgba)
draw_line(v1, v2, rgba)
# -------------------------------------------------------------
# Draw an OpenGL Rectangle
#
# v1, v2 are corners (bottom left / top right)
# -------------------------------------------------------------
def draw_rectangle(v1, v2, rgba):
# noinspection PyBroadException
try:
if v1 is not None and v2 is not None:
v1b = (v2[0], v1[1])
v2b = (v1[0], v2[1])
draw_line(v1, v1b, rgba)
draw_line(v1b, v2, rgba)
draw_line(v2, v2b, rgba)
draw_line(v2b, v1, rgba)
except:
pass
# -------------------------------------------------------------
# format a point as (x, y, z) for display
#
# -------------------------------------------------------------
def format_point(mypoint, pr):
pf = "%1." + str(pr) + "f"
fmt = " ("
fmt += pf % mypoint[0]
fmt += ", "
fmt += pf % mypoint[1]
fmt += ", "
fmt += pf % mypoint[2]
fmt += ")"
return fmt
# -------------------------------------------------------------
# Draw object num for debug
#
# -------------------------------------------------------------
# noinspection PyUnresolvedReferences,PyUnboundLocalVariable,PyUnusedLocal
def draw_object(context, myobj, region, rv3d):
scene = bpy.context.scene
rgba = scene.measureit_debug_obj_color
fsize = scene.measureit_debug_font
precision = scene.measureit_debug_precision
# --------------------
# object Loop
# --------------------
objs = bpy.context.scene.objects
obidxs = list(range(len(bpy.context.scene.objects)))
for o in obidxs:
# Display only selected
if scene.measureit_debug_select is True:
if objs[o].select_get() is False:
continue
a_p1 = Vector(get_location(objs[o]))
# Text
txt = ''
if scene.measureit_debug_objects is True:
txt += str(o)
if scene.measureit_debug_object_loc is True:
txt += format_point(a_p1, precision)
# converting to screen coordinates
txtpoint2d = get_2d_point(region, rv3d, a_p1)
draw_text(myobj, txtpoint2d, txt, rgba, fsize)
return
# -------------------------------------------------------------
# Draw vertex num for debug
#
# -------------------------------------------------------------
# noinspection PyUnresolvedReferences,PyUnboundLocalVariable,PyUnusedLocal
def draw_vertices(context, myobj, region, rv3d):
# Only meshes
if myobj.type != "MESH":
return
scene = bpy.context.scene
rgba = scene.measureit_debug_vert_color
fsize = scene.measureit_debug_font
precision = scene.measureit_debug_precision
# --------------------
# vertex Loop
# --------------------
if scene.measureit_debug_vert_loc_toggle == '1':
co_mult = lambda c: c
else: # if global, convert local c to global
co_mult = lambda c: myobj.matrix_world @ c
if myobj.mode == 'EDIT':
bm = from_edit_mesh(myobj.data)
obverts = bm.verts
else:
obverts = myobj.data.vertices
for v in obverts:
# Display only selected
if scene.measureit_debug_select is True:
if v.select is False:
continue
# noinspection PyBroadException
# try:
a_p1 = get_point(v.co, myobj)
# converting to screen coordinates
txtpoint2d = get_2d_point(region, rv3d, a_p1)
# Text
txt = ''
if scene.measureit_debug_vertices is True:
txt += str(v.index)
if scene.measureit_debug_vert_loc is True:
txt += format_point(co_mult(v.co), precision)
draw_text(myobj, txtpoint2d, txt, rgba, fsize)
# except:
# print("Unexpected error:" + str(exc_info()))
# pass
return
# -------------------------------------------------------------
# Draw edge num for debug
#
# -------------------------------------------------------------
# noinspection PyUnresolvedReferences,PyUnboundLocalVariable,PyUnusedLocal
def draw_edges(context, myobj, region, rv3d):
# Only meshes
if myobj.type != "MESH":
return
scene = bpy.context.scene
rgba = scene.measureit_debug_edge_color
fsize = scene.measureit_debug_font
precision = scene.measureit_debug_precision
# --------------------
# edge Loop
#
# uses lambda for edge midpoint finder (midf) because edit mode
# edge vert coordinate is not stored in same places as in obj mode
# --------------------
if myobj.mode == 'EDIT':
bm = from_edit_mesh(myobj.data)
obedges = bm.edges
obverts = None # dummy value to avoid duplicating for loop
midf = lambda e, v: e.verts[0].co.lerp(e.verts[1].co, 0.5)
else:
obedges = myobj.data.edges
obverts = myobj.data.vertices
midf = lambda e, v: v[e.vertices[0]].co.lerp(v[e.vertices[1]].co, 0.5)
for e in obedges:
# Display only selected
if scene.measureit_debug_select is True:
if e.select is False:
continue
a_mp = midf(e, obverts)
a_p1 = get_point(a_mp, myobj)
# converting to screen coordinates
txtpoint2d = get_2d_point(region, rv3d, a_p1)
draw_text(myobj, txtpoint2d, str(e.index), rgba, fsize)
return
# -------------------------------------------------------------
# Draw face num for debug
#
# -------------------------------------------------------------
# noinspection PyUnresolvedReferences,PyUnboundLocalVariable,PyUnusedLocal
def draw_faces(context, myobj, region, rv3d):
# Only meshes
if myobj.type != "MESH":
return
scene = bpy.context.scene
rgba = scene.measureit_debug_face_color
rgba2 = scene.measureit_debug_norm_color
fsize = scene.measureit_debug_font
ln = scene.measureit_debug_normal_size
th = scene.measureit_debug_width
precision = scene.measureit_debug_precision
# --------------------
# face Loop
# --------------------
if myobj.mode == 'EDIT':
bm = from_edit_mesh(myobj.data)
obverts = bm.verts
myfaces = bm.faces
else:
obverts = myobj.data.vertices
myfaces = myobj.data.polygons
for f in myfaces:
normal = f.normal
# Display only selected
if scene.measureit_debug_select is True:
if f.select is False:
continue
# noinspection PyBroadException
try:
if myobj.mode == 'EDIT':
a_p1 = get_point(f.calc_center_median(), myobj)
else:
a_p1 = get_point(f.center, myobj)
a_p2 = (a_p1[0] + normal[0] * ln, a_p1[1] + normal[1] * ln, a_p1[2] + normal[2] * ln)
# line setup
gpu.state.blend_set('ALPHA')
imm_set_line_width(th)
# converting to screen coordinates
txtpoint2d = get_2d_point(region, rv3d, a_p1)
point2 = get_2d_point(region, rv3d, a_p2)
# Text
if scene.measureit_debug_faces is True:
draw_text(myobj, txtpoint2d, str(f.index), rgba, fsize)
# Draw Normal
if scene.measureit_debug_normals is True:
gpu.state.blend_set('ALPHA')
draw_arrow(txtpoint2d, point2, rgba, 10, "99", "1")
if len(obverts) > 2 and scene.measureit_debug_normal_details is True:
if myobj.mode == 'EDIT':
i1 = f.verts[0].index
i2 = f.verts[1].index
i3 = f.verts[2].index
else:
i1 = f.vertices[0]
i2 = f.vertices[1]
i3 = f.vertices[2]
a_p1 = get_point(obverts[i1].co, myobj)
a_p2 = get_point(obverts[i2].co, myobj)
a_p3 = get_point(obverts[i3].co, myobj)
# converting to screen coordinates
a2d = get_2d_point(region, rv3d, a_p1)
b2d = get_2d_point(region, rv3d, a_p2)
c2d = get_2d_point(region, rv3d, a_p3)
# draw vectors
draw_arrow(a2d, b2d, rgba, 10, "99", "1")
draw_arrow(b2d, c2d, rgba, 10, "99", "1")
# Normal vector data
txt = format_point(normal, precision)
draw_text(myobj, point2, txt, rgba2, fsize)
except:
print("Unexpected error:" + str(exc_info()))
pass
return
# --------------------------------------------------------------------
# Distance between 2 points in 3D space
# v1: first point
# v2: second point
# locx/y/z: Use this axis
# return: distance
# --------------------------------------------------------------------
def distance(v1, v2, locx=True, locy=True, locz=True):
x = sqrt((v2[0] - v1[0]) ** 2 + (v2[1] - v1[1]) ** 2 + (v2[2] - v1[2]) ** 2)
# If axis is not used, make equal both (no distance)
v1b = [v1[0], v1[1], v1[2]]
v2b = [v2[0], v2[1], v2[2]]
if locx is False:
v2b[0] = v1b[0]
if locy is False:
v2b[1] = v1b[1]
if locz is False:
v2b[2] = v1b[2]
xloc = sqrt((v2b[0] - v1b[0]) ** 2 + (v2b[1] - v1b[1]) ** 2 + (v2b[2] - v1b[2]) ** 2)
return x, xloc
# --------------------------------------------------------------------
# Interpolate 2 points in 3D space
# v1: first point
# v2: second point
# d1: distance
# return: interpolate point
# --------------------------------------------------------------------
def interpolate3d(v1, v2, d1):
# calculate vector
v = (v2[0] - v1[0], v2[1] - v1[1], v2[2] - v1[2])
# calculate distance between points
d0, dloc = distance(v1, v2)
# calculate interpolate factor (distance from origin / distance total)
# if d1 > d0, the point is projected in 3D space
if d0 > 0:
x = d1 / d0
else:
x = d1
final = (v1[0] + (v[0] * x), v1[1] + (v[1] * x), v1[2] + (v[2] * x))
return final
# --------------------------------------------------------------------
# Get point rotated and relative to parent
# v1: point
# mainobject
# --------------------------------------------------------------------
def get_point(v1, mainobject):
# Using World Matrix
vt = Vector((v1[0], v1[1], v1[2], 1))
m4 = mainobject.matrix_world
vt2 = m4 @ vt
v2 = [vt2[0], vt2[1], vt2[2]]
return v2
# --------------------------------------------------------------------
# Get location in world space
# v1: point
# mainobject
# --------------------------------------------------------------------
def get_location(mainobject):
# Using World Matrix
m4 = mainobject.matrix_world
return [m4[0][3], m4[1][3], m4[2][3]]
# --------------------------------------------------------------------
# Get vertex data
# mainobject
# --------------------------------------------------------------------
def get_mesh_vertices(myobj):
try:
if myobj.mode == 'EDIT':
bm = from_edit_mesh(myobj.data)
obverts = bm.verts
else:
obverts = myobj.data.vertices
return obverts
except AttributeError:
return None
# --------------------------------------------------------------------
# Get position for scale text
#
# --------------------------------------------------------------------
def get_scale_txt_location(context):
scene = context.scene
pos_x = int(context.region.width * scene.measureit_scale_pos_x / 100)
pos_y = int(context.region.height * scene.measureit_scale_pos_y / 100)
return pos_x, pos_y
# --------------------------------------------------------------------
# Get position in final render image
# (Z < 0 out of camera)
# return 2d position
# --------------------------------------------------------------------
def get_render_location(mypoint):
v1 = Vector(mypoint)
scene = bpy.context.scene
co_2d = object_utils.world_to_camera_view(scene, scene.camera, v1)
# Get pixel coords
render_scale = scene.render.resolution_percentage / 100
render_size = (int(scene.render.resolution_x * render_scale),
int(scene.render.resolution_y * render_scale))
return [round(co_2d.x * render_size[0]), round(co_2d.y * render_size[1])]
# ---------------------------------------------------------
# Get center of circle base on 3 points
#
# Point a: (x,y,z) arc start
# Point b: (x,y,z) center
# Point c: (x,y,z) midle point in the arc
# Point d: (x,y,z) arc end
# Return:
# ang: angle (radians)
# len: len of arc
#
# ---------------------------------------------------------
def get_arc_data(pointa, pointb, pointc, pointd):
v1 = Vector((pointa[0] - pointb[0], pointa[1] - pointb[1], pointa[2] - pointb[2]))
v2 = Vector((pointc[0] - pointb[0], pointc[1] - pointb[1], pointc[2] - pointb[2]))
v3 = Vector((pointd[0] - pointb[0], pointd[1] - pointb[1], pointd[2] - pointb[2]))
angle = v1.angle(v2) + v2.angle(v3)
rclength = pi * 2 * v2.length * (angle / (pi * 2))
return angle, rclength
# -------------------------------------------------------------
# Format a number to the right unit
#
# -------------------------------------------------------------
def format_distance(fmt, units, value, factor=1):
s_code = "\u00b2" # Superscript two
hide_units = bpy.context.scene.measureit_hide_units
# ------------------------
# Units automatic
# ------------------------
if units == "1":
# Units
if bpy.context.scene.unit_settings.system == "IMPERIAL":
feet = value * (3.2808399 ** factor)
if round(feet, 2) >= 1.0:
if hide_units is False:
fmt += " ft"
if factor == 2:
fmt += s_code
tx_dist = fmt % feet
else:
inches = value * (39.3700787 ** factor)
if hide_units is False:
fmt += " in"
if factor == 2:
fmt += s_code
tx_dist = fmt % inches
elif bpy.context.scene.unit_settings.system == "METRIC":
if round(value, 2) >= 1.0:
if hide_units is False:
fmt += " m"
if factor == 2:
fmt += s_code
tx_dist = fmt % value
else:
if round(value, 2) >= 0.01:
if hide_units is False:
fmt += " cm"
if factor == 2:
fmt += s_code
d_cm = value * (100 ** factor)
tx_dist = fmt % d_cm
else:
if hide_units is False:
fmt += " mm"
if factor == 2:
fmt += s_code
d_mm = value * (1000 ** factor)
tx_dist = fmt % d_mm
else:
tx_dist = fmt % value
# ------------------------
# Units meters
# ------------------------
elif units == "2":
if hide_units is False:
fmt += " m"
if factor == 2:
fmt += s_code
tx_dist = fmt % value
# ------------------------
# Units centimeters
# ------------------------
elif units == "3":
if hide_units is False:
fmt += " cm"
if factor == 2:
fmt += s_code
d_cm = value * (100 ** factor)
tx_dist = fmt % d_cm
# ------------------------
# Units millimeters
# ------------------------
elif units == "4":
if hide_units is False:
fmt += " mm"
if factor == 2:
fmt += s_code
d_mm = value * (1000 ** factor)
tx_dist = fmt % d_mm
# ------------------------
# Units feet
# ------------------------
elif units == "5":
if hide_units is False:
fmt += " ft"
if factor == 2:
fmt += s_code
feet = value * (3.2808399 ** factor)
tx_dist = fmt % feet
# ------------------------
# Units inches
# ------------------------
elif units == "6":
if hide_units is False:
fmt += " in"
if factor == 2:
fmt += s_code
inches = value * (39.3700787 ** factor)
tx_dist = fmt % inches
# ------------------------
# Default
# ------------------------
else:
tx_dist = fmt % value
return tx_dist
# -------------------------------------------------------------
# Get radian float based on angle choice
#
# -------------------------------------------------------------
def get_angle_in_rad(fangle):
if fangle == 0:
return 0.0
else:
return radians(fangle)