Skip to content
Snippets Groups Projects
measureit_geometry.py 59.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • # 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('2D_UNIFORM_COLOR') if not bpy.app.background else None
    
    shader_line = gpu.shader.from_builtin('3D_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
            # --------------------
    
    NBurn's avatar
    NBurn committed
            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
    
                    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
    
                        # ----------------------
                        # 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]))
    
                            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)
    
                            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)
    
                                    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')
    
                            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)
    
                            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)
    
                                    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
    
                            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
    
    NBurn's avatar
    NBurn committed
                            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")
    
                                    draw_arrow(screen_point_p1, screen_point_p2, rgba, ms.glarc_s, "99", ms.glarc_b)
    
                                    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:
    
    NBurn's avatar
    NBurn committed
                                    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:
    
    NBurn's avatar
    NBurn committed
                                    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)
    
                        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)
    
    NBurn's avatar
    NBurn committed
                    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 = 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 = get_triangle_area(p1, p2, p3, ms_scale)
    
        # 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)
    
        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)
    
    NBurn's avatar
    NBurn committed
            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
    
        x_pos, y_pos = pos2d
    
        ui_scale = bpy.context.preferences.system.ui_scale
    
        blf.size(font_id, round(fsize * ui_scale), 72)
    
        # blf.size(font_id, fsize, dpi)
    
        # 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)
    
            draw_line(v2, v2a, rgba)
            draw_line(v2, v2b, rgba)
    
            draw_triangle(v1, v1a, v1b, rgba)
    
            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 += ")"