Skip to content
Snippets Groups Projects
measureit_geometry.py 59.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • # -------------------------------------------------------------
    # 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:
    
            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)
    
    # -------------------------------------------------------------
    # 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
    
            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
    
            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)
    
                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)
    
                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))
    
        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]))
    
        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
        # ------------------------
    
        # ------------------------
        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)