# 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)
