##  Beam_HSS_Fill.py Version 1.06

##  Copyright (c) 2006 Bruce Vaughan, BV Detailing & Design, Inc.

##  All rights reserved.

##  NOT FOR SALE. The software is provided "as is" without any warranty.

############################################################################

"""

/// Beam_HSS_Fill.py Version 1.05

/// This script adds HSS fill material to the top of beams. HSS fills are typical

/// in commercial construction where joists are used. It could easily be adapted

/// for angle fill material.

///

/// OPTIONS:

/// 1. Select the beam and select the joists to fill between. User can be in any

///    model orientation. The joists can be at any skew or slope. Joists can be

///    selected in any order.

/// 2. Select the beam and pick points to fill between. User must be in plan or

///    elevation. Points can be picked in any order.

/// 3. Select the beam only for full length fill. The length is not rounded off

///    regardless of the value of 'hss_length_rnd'.

///

/// User has the option of offsetting the HSS material in the beam 'z' direction.

///

/// User has the option of adding fill material to all members with the same piecemark.

///

/// User has the option of selecting the minimum length of fill material to add.

///

/// User can select the material length rounding increment. Length is always rounded down.

///

/// The code is in place to add welds, but SDS/2 intermittent welds do not work

/// when the weld profile is a flair groove.

///

/// Required files from folder 'macrolib' on SDS/2 system path:

///     pickle.py

///     FileDefaults

///     MemCnt.py

///     L3D.py

///     angle.py

///     round_length.py

///     ExceptWarn.py

///     Weld.py

///

/// Required folders:

///     "system path"/macrolib

///     "system path"/Defaults

///     "system path"/Images

///

/// Revision History:

///     Version 1.00 (10/23/06) - Initial release

///     Version 1.01 (10/24/06) - Import module round_length, delete function round_length_last

///                               Add member 'z' offset option

///                               Added 'a_rad' to x_list

///     Version 1.02 (10/24/06) - 'if a.not_parallel == 1:' >> 'if a.not_parallel() == 1:'

///                               Moved calculation of 'mem_pt' and 'a_rad' below 'if a.not_parallel() == 1:'

///     Version 1.03 (10/25/06) - Add 'yes_or_no' at end of main while loop to confirm continuation.

///     Version 1.04 (10/27/06) - Incorporate an exception handler (formatExceptionInfo)

///     Version 1.05 (11/2/06) -  Import mtrl_weld() from Weld

///     Version 1.06 (11/27/06) - Eliminate imports string and types

///   ************************************************************

/// ****NOTE: This version is not compatible with SDS/2 6.3.****

/// ************************************************************                    

///   

/// Version 0.99 was developed and tested in SDS2 7.023 (October 22 & 23, 2006) by Bruce Vaughan

/// Detailing & Design, Inc. (BVD)  (615) 646-9239

/// For comments, suggestions or questions call BV at the number above, email bvaughan@bvdetailing

/// or post to SDS/2 forum.

///

/// Go to Defaults section to modify script defaults.

/// Go to Variables section to modify paths to image and default files

"""

def run_script():

    try:

        # startup code begin

        from math import cos, floor, pi, tan

        from param import yes_or_no, ResponseNotOK, Units, ClearSelection, Dialog, dim_print, Warning, dim

        from macrolib.FileDefaults import import_data, export_data  # macrolib.pickle required

        from macrolib.MemCnt import member_count

        from macrolib.L3D import LineLineIntersect3D

        from macrolib.angle import rtod, dtor, angle_between_members

        from macrolib.round_length import round_length_last

        from macrolib.ExceptWarn import formatExceptionInfo

        from macrolib.Weld import mtrl_weld

       

        import os

        import sys

        Units("feet")

        from shape import Shape

        from point import Point, PointLocate

        from member import Member, MemberLocate, MemberAllocated

        from rolled_section import RolledSection

        from weld_add import Weld

        from job import Job

        from fab import Fabricator

        # startup code end

        #################################################################

        drv_letter, drv_path = os.path.splitdrive(os.getcwd())

        ## Variables section

        # system path for defaults file

        default_file_path = os.path.join(os.getcwd(), "macro", "Defaults")

        # defaults file

        def_file = "Beam_HSS_Fill_v1_06.txt"

        image_path = os.path.join(os.getcwd(), "macro", "Images")

        image_name1 = os.path.join(image_path, "Beam_HSS_Fill_1.gif")

        image_name2 = os.path.join(image_path, "Beam_HSS_Fill_2.gif")

        image_name3 = os.path.join(image_path, "Beam_HSS_Fill_3.gif")

        image_name4 = os.path.join(image_path, "Beam_HSS_Fill_4.gif")

        image_name5 = os.path.join(image_path, "Beam_HSS_Fill_5.gif")

        image_name6 = os.path.join(image_path, "Beam_HSS_Fill_6.gif")

        image_name7 = os.path.join(image_path, "Beam_HSS_Fill_7.gif")

        # default to enable or disable the importing and exporting of dialog dictionary variables "Enable" "Disable"

        enable_default_import_export = "Enable"    

        ## Defaults section

        min_length = 12.0

        hss_size = "HSS2 1/2x2 1/2x1/4"

        hss_jst_clr = 4.0

        hss_pt_clr_left = 4.0

        hss_pt_clr_right = 4.0

        hss_length_rnd = "1/2"   #["0", "1/16", "1/8", "1/4", "1/2", "1"]

        # "Long Side Vert", "Long Side Horiz" applies to rectangular hss

        hss_orient = "Long Side Vert"

        # Job().steel_grades("Tube").keys()

        hss_grade = "A500B"

        # "Red Oxide", "Yellow Zinc", "Gray Oxide", "Sandblasted", "Blued Steel", "Galvanized"

        hss_finish = "Gray Oxide"

        add_hss_left = "Yes"

        add_hss_right = "Yes"

        clr_field_weld_prep = 3.0       # clearance from field weld preparatons

        z_off = -1.25

        ## WELDING DEFAULTS - TAIL NOTE IS NOT SUPPORTED IN THIS VERSION

        add_hss_weld = "Yes"                        # "Yes", "No"

        ## weld type is limited to 'Fillet' and 'Flair Bevel'

        hss_weld_type = "Flair Bevel"               # "Fillet", "Flair Bevel"

        ## Weldsize is common to bent plate weld and closure weld

        weld_size = 0.1875                          # "1/8", "3/16", "1/4"

        ## Intermittent only applies to bent plate weld

        weld_stagger = "Opposite"                   # "No" "Opposite" "Offset"

        weld_inter_length = 2.0

        weld_inter_spa = 12.0

        ## End Defaults and Variables sections

        #############################################################################

        ## Function definition - add a rolled shape, return the rolled shape if successful

        ##                       or return 0 if unsuccessful

        def add_hss_mtrl(mem, pnt1, pnt2, matl_size, hss_gr, hss_fin, hss_orientation):

            if Shape(matl_size).long_depth != Shape(matl_size).short_depth:

                mu = "Beam_HSS_Fill_RT"

                if hss_orientation == "Long Side Horiz":

                    rotate_arg = (90.0, 0.0, 0.0)

                    ctr_ref = "Yes"

                    pnt1 = pnt1 + mem.translate(0.0, - Shape(matl_size).short_depth/2.0, 0.0)

                    pnt2 = pnt2 + mem.translate(0.0, - Shape(matl_size).short_depth/2.0, 0.0)

                else:

                    rotate_arg = (0.0, 0.0, 0.0)

                    ctr_ref = "No"

            else:

                rotate_arg = (0.0, 0.0, 0.0)

                ctr_ref = "No"

                mu = "Beam_HSS_Fill_SQ"

            try:

                # rolled section begin

                rl1 = RolledSection()

                rl1.member = mem

                rl1.pt1 = pnt1

                rl1.pt2 = pnt2

                rl1.section_size = matl_size

                rl1.grade = hss_gr

                rl1.centered = ctr_ref

                # rl1.top_oper_left = "None"

                rl1.top_cope_left = 0.0   

                rl1.top_oper_right = "None"

                rl1.bottom_oper_left = "None"

                rl1.bottom_length_left = 0.0

                rl1.bottom_cope_left = 0.0

                rl1.bottom_oper_right = "None"

                rl1.llv = "HZ."

                rl1.toe_io = "In"

                rl1.rolling_op = "None"

                rl1.width = 0

                rl1.thick = 0

                rl1.field_weld_prep_left = "No"

                rl1.field_weld_prep_right = "No"

                rl1.cut_radius_left = 0.5

                rl1.cut_radius_right = 0.5

                rl1.web_setback_left = 0

                rl1.web_setback_right = 0

                rl1.angle_of_twist = 0

                rl1.mid_ordinate = 0

                rl1.bend_angle = 0

                rl1.bend_radius = 0

                rl1.rolled_offset = 0

                rl1.work_pt_dist = pnt1.dist(pnt2)

                rl1.setback_left = 0

                rl1.setback_right = 0

                rl1.web_cut_angle_left = 0

                rl1.web_cut_angle_right = 0

                rl1.flange_cut_left = 0

                rl1.flange_cut_right = 0

                rl1.end_cut_left = "Standard Cut"

                rl1.end_cut_right = "Standard Cut"

                rl1.length = rl1.work_pt_dist - rl1.setback_left - rl1.setback_right

                rl1.mtrl_type = Shape(matl_size).type()

                rl1.finish = hss_fin

                rl1.mtrl_usage = mu

                rl1.ref_pt_offset = (0.000000, 0.000000, 0.000000)

                rl1.add()

                rl1.rotate(rl1.member, rotate_arg)

            # rolled section end

            except:

                Warning("Material add problem")

                return 0

            else:

                return rl1

        #################################################################

        ## import defaults data if enabled

        if enable_default_import_export == "Enable":

            dd0 = import_data(os.path.join(default_file_path, def_file))

            if dd0:

                if isinstance(dd0, list):

                    for dd in dd0:

                        for key, value in dd.items():

                            exec "%s = %s" % (key, repr(value)) in None

                elif isinstance(dd0, dict):

                    for key, value in dd0.items():

                        exec "%s = %s" % (key, repr(value)) in None

                else:

                    Warning("Invalid data - Reverting to original defaults")

        #################################################################

        ## main program loop

        while 1:

            # select beam member to add hss filler material

            ClearSelection()

            bm_list = []

            while 1:

                mem1 = MemberLocate("Select BEAM Member to Add HSS Filler Material")

                if mem1 == None:

                    break

                else:

                    if mem1.type == "Beam":

                        if mem1.mtrl_type in ["W flange", "Tube", "Channel", "S Shape"]:

                            break           # Limit selection to one member

                        else:

                            Warning(mem1.mtrl_type + " beam material is not supported.")

                    else:

                        Warning("You picked a " + mem1.type + ". You must pick a Beam.")

            if mem1 == None:

                break

            else:

                bm_list.append(mem1)

            chk_mem_list = member_count(mem1)

            if len(chk_mem_list) > 1:

                if yes_or_no("Add hss fillers to all members with the same piecemark (%s members with mark %s)?" % \

                             (len(chk_mem_list), bm_list[0].piecemark)) == 1:

                    bm_list = chk_mem_list

            #############################################################

            # select joist members

            jst_list = []

            while 1:  

                mem = MemberLocate("Select a beam or joist member or members")

                if not mem:

                    break

                else:

                    if mem.type in ["Beam", "Joist"]:

                        jst_list.append(mem)

                    else:

                        Warning("You picked a " + mem.type + ". \nThe member was not added to the selection set.")

            # Loop to check for valid material size

            while 1:

                #############################################################

                ## DIALOG BOX 1 --------------------------------------------#

                #############################################################

                dlg1 = Dialog("Add hss fill material to beam members")

                dlg1.menu("print_doc", ("Yes", "No"), "No", "Print parametric script documentation only         ")

                dlg1.tabset_begin()

                dlg1.tab("General Information")

                dlg1.column_group_begin()

                dlg1.column(0)

                dlg1.group_title("General")

                dlg1.entry("hss_size", hss_size, "HSS material size                 " )

                dlg1.menu("hss_finish", ("None", "Red Oxide", "Yellow Zinc", "Gray Oxide", "Sandblasted", "Blued Steel", "Galvanized"), hss_finish, "Material finish")

                dlg1.menu("hss_grade", Job().steel_grades("Tube").keys(), hss_grade, "Material grade" )

                dlg1.line("'Long Side Vert' or 'Long Side Horiz' applies to rectangular HSS material")

                dlg1.menu("hss_orient", ("Long Side Vert", "Long Side Horiz"), hss_orient, "Material toe direction where applicable")

                dlg1.group_title("HSS filler clearance from selected JOISTS")

                dlg1.entry("hss_jst_clr", dim_print(hss_jst_clr), "Joist clearance")

                dlg1.group_title("HSS filler clearance from picked POINTS")

                dlg1.entry("hss_pt_clr_left", dim_print(hss_pt_clr_left), "Left clearance from picked point")

                dlg1.entry("hss_pt_clr_right", dim_print(hss_pt_clr_right), "Right clearance from picked point")

                dlg1.group_title("Additional options")

                dlg1.entry("min_length", dim_print(min_length), "Minimum length of HSS filler to add to beam")

                dlg1.entry("z_off", dim_print(z_off), "Mtrl offset in BEAM 'z' direction")

                dlg1.menu("hss_length_rnd", ["0", "1/16", "1/8", "1/4", "1/2", "1"], hss_length_rnd, "Round HSS material length down to increment")

                dlg1.menu("add_hss_left", ["Yes", "No"], add_hss_left, "Add left end HSS fill material")

                dlg1.menu("add_hss_right", ["Yes", "No"], add_hss_right, "Add right end HSS fill material")

                dlg1.column(0)

                dlg1.group_title("Left End HSS")

                dlg1.image(image_name3)

                dlg1.column_group_end()

               

                dlg1.tab("Weld")

                dlg1.image(image_name1)

                dlg1.image(image_name2)

                dlg1.group_title("HSS Weld")

                dlg1.menu("add_hss_weld", ("Yes", "No"), add_hss_weld, "Add weld to HSS filler material                             ")

                dlg1.menu("hss_weld_type", ("Fillet", "Flair Bevel"), hss_weld_type, "Weld type")

                dlg1.menu("weld_size", (dim_print(0.125), dim_print(0.1875), dim_print(0.25)), weld_size, "Weld size")

                dlg1.group_title_end

                dlg1.group_title("Continuous or Intermittent HSS Weld")

                dlg1.menu("weld_stagger", ("No", "Opposite", "Offset"), weld_stagger, "Intermittent weld to bent plates")

                dlg1.entry("weld_inter_length", weld_inter_length, "Intermittent weld length")

                dlg1.entry("weld_inter_spa", weld_inter_spa, "Intermittent weld spacing")

                dlg1.group_title_end

               

                dlg1.tab("Image 1")

                dlg1.column_group_begin()

                dlg1.column(0)

                dlg1.group_title("Plan View")

                dlg1.image(image_name5)

                dlg1.column(0)

                dlg1.group_title("Left end at cope edge")

                dlg1.image(image_name6)

                dlg1.column(0)

                dlg1.group_title("Long Leg Vert")

                dlg1.image(image_name4)

                dlg1.column_group_end()

               

                dlg1.tab("Image 2")

                dlg1.group_title("Right end clear of flange preparation")

                dlg1.image(image_name7)

                dlg1.tabset_end()

                try:

                    dd1 = dlg1.done()

                except ResponseNotOK:

                    cancel_dd1 = "Yes"

                    break

                else:

                    cancel_dd1 = "No"

                for key, value in dd1.items():

                    exec "%s = %s" % (key, repr(value)) in None

                #############################################################

                ## END DIALOG BOX 1 ----------------------------------------#

                #############################################################

                try:

                    Shape(hss_size)

                except:             # size is invalid

                    Warning("The material size is invalid. Hit 'OK' to continue.")

                else:               # size is valid, break from while loop

                    break

           

            if cancel_dd1 == "Yes":

                break

 

            # If hss_size = square tube set variable 'hss_orient' to 'Long Side Vert'

            if Shape(hss_size).long_depth == Shape(hss_size).short_depth:

                hss_orient = "Long Side Vert"

                dd1['hss_orient'] = "Long Side Vert"

 

            # Set vert_width and horiz_width variables for later use       

            if hss_orient == "Long Side Vert":

                vert_width = Shape(hss_size).long_depth

                horiz_width = Shape(hss_size).short_depth

            else:

                vert_width = Shape(hss_size).short_depth

                horiz_width = Shape(hss_size).long_depth

 

            # Export defaults to disk if enabled

            if enable_default_import_export == "Enable":

                export_data(os.path.join(default_file_path, def_file), dd1)

           

            if print_doc == "Yes":

                print __doc__

                break

 

            ## Initialize x_list and pt_list

            ## Each item in 'x_list' will be a list of:

            ## ['beam 'x' distances from the left end WP', 'complement of angle between members']

            ## each 'complement of angle between members' will be set to 0.0 if no joists were selected

            x_list = []       

            pt_list = []

            # Get points if no joists were selected

            if len(jst_list) == 0:

                while 1:

                    pt1 = PointLocate("Select HSS POINT locations")

                    if pt1 == None:

                        break

                    else:

                        pt_list.append(pt1)

                # complement to the net angle between the beam and joist = 0.0 if jst_list is empty

                a_rad = 0.0

                # convert points to list of local member 'x' distance values and a_rad

                for pt in pt_list:

                    x_list.append([(bm_list[0].trans_to_local(pt - bm_list[0].left_location)).x, a_rad])

                x_list.sort()

                # set clearances at left and right end of HSS material

                clr_left = hss_pt_clr_left

                clr_right = hss_pt_clr_right

            # populate 'x_list' with adjusted apparent intersection points and each 'complement of angle between members'

            else:

                x_list = []

                for i in range(len(jst_list)):

                    pt1, pt2 = bm_list[0].left_location, bm_list[0].right_location

                    pt3, pt4 = jst_list[i].left_location, jst_list[i].right_location

                    a = LineLineIntersect3D(pt1, pt2, pt3, pt4)

                    """

                    /// When the beam and joists are not in parallel planes, the unit vector of the apparent intersection points

                    /// may not be normal to the beam. This will cause an offset which can be corrected by calculating the beam

                    /// member coordinates of 'a.Pmem2' and adjusting for the resulting triangle.

                    """

                    # Check for a parallel joist and for a joist that does not intersect the beam

                    if a.not_parallel() == 1:

                        a_rad = angle_between_members(bm_list[0], jst_list[i])

                        mem_pt = bm_list[0].trans_to_local(a.Pmem2-bm_list[0].left.location)

                        if a.position == "Not Beyond LE" or a.position == "Not Beyond RE":

                            x_list.append([(a.left_dist - ((mem_pt.z-z_off)*tan(a_rad))), a_rad])

                        else:

                            Warning("One of the joists you selected does not intersect the beam. \nThis intersection point will be discarded.")

                    else:

                        Warning("***Invalid selection***\nOne of the joists you selected is parallel to the beam.")

                x_list.sort()

                # set clearances at left and right end of HSS material

                clr_left = hss_jst_clr

                clr_right = hss_jst_clr

               

            # calculate dist_left and dist_right (distances from beam WP to end of HSS material)

            if bm_list[0].main_mtrl().top_oper_left in ["Cope plain", "Cut flange flush"]:

                dist_left = bm_list[0].left.setback + bm_list[0].main_mtrl().top_length_left

            elif bm_list[0].main_mtrl().top_oper_left in ["Cope field weld", "FEMA Cope field weld", "Cope shop weld", "FEMA Cope shop weld"]:

                dist_left = bm_list[0].left.setback + bm_list[0].main_mtrl().top_length_left + clr_field_weld_prep

            else:

                dist_left = bm_list[0].left.setback

           

            if bm_list[0].main_mtrl().top_oper_right in ["Cope plain", "Cut flange flush"]:

                dist_right = bm_list[0].right.setback + bm_list[0].main_mtrl().top_length_right

            elif bm_list[0].main_mtrl().top_oper_right in ["Cope field weld", "FEMA Cope field weld", "Cope shop weld", "FEMA Cope shop weld"]:

                dist_right = bm_list[0].right.setback + bm_list[0].main_mtrl().top_length_right + clr_field_weld_prep

            else:

                dist_right = bm_list[0].right.setback

               

            # create a list of point pairs in member coordinates and add HSS filler material for each pair if material length > min_length

            # initialize pt_list

            pt_pairs = []

            if len(x_list) == 0:

                # if no joists or points were selected, add one HSS filler material full length regardless of min_length or length rounding

                pt_pairs.append([Point(dist_left, vert_width, z_off), Point(bm_list[0].input_length-dist_right, vert_width, z_off)])

            else:

                if add_hss_left == "Yes":       # add left end HSS mtrl - the left end will align with the main material cope edge

                    calc_right_clr = (clr_right/cos(abs(x_list[0][1])) + horiz_width/2*tan(abs(x_list[0][1])))

                    mtrl_length = round_length_last((x_list[0][0] - dist_left - calc_right_clr), hss_length_rnd)

                    if mtrl_length >= min_length:

                        pt_pairs.append([Point(dist_left, vert_width, z_off), Point(dist_left + mtrl_length, vert_width, z_off)])                   

 

                for i in range(len(x_list)-1):  # add HSS material between joists or between picked points

                    calc_left_clr = clr_left/cos(abs(x_list[i][1])) + horiz_width/2*tan(abs(x_list[i][1]))

                    calc_right_clr = clr_right/cos(abs(x_list[i+1][1])) + horiz_width/2*tan(abs(x_list[i+1][1]))

                    calc_mtrl_length = (x_list[i+1][0] - x_list[i][0] - calc_left_clr - calc_right_clr)

                    mtrl_length = round_length_last(calc_mtrl_length, hss_length_rnd)

                    if mtrl_length >= min_length:

                        first_pt = Point(x_list[i][0] + calc_left_clr + ((calc_mtrl_length-mtrl_length)/2.0), vert_width, z_off)

                        second_pt = Point(x_list[i+1][0] - calc_right_clr - ((calc_mtrl_length-mtrl_length)/2.0), vert_width, z_off)

                        pt_pairs.append([first_pt, second_pt])

                   

                if add_hss_right == "Yes":   # add right end HSS mtrl - the right end will align with the main material cope edge

                    calc_left_clr = (clr_left/cos(abs(x_list[-1][1])) + horiz_width/2*tan(abs(x_list[-1][1])))

                    mtrl_length = round_length_last((bm_list[0].input_length - x_list[-1][0] - dist_right - calc_left_clr), hss_length_rnd)

                    if mtrl_length >= min_length:

                        first_pt = Point((bm_list[0].input_length - dist_right - mtrl_length), vert_width, z_off)

                        second_pt = Point((bm_list[0].input_length - dist_right), vert_width, z_off)

                        pt_pairs.append([first_pt, second_pt])

                       

            for mem1 in bm_list:

                for pp in pt_pairs:

                    pt1 = mem1.left_location + mem1.translate(pp[0].x, pp[0].y, pp[0].z)

                    pt2 = mem1.left_location + mem1.translate(pp[1].x, pp[1].y, pp[1].z)

                    hss1 = add_hss_mtrl(mem1, pt1, pt2, hss_size, hss_grade, hss_finish, hss_orient)

                    if add_hss_weld == "Yes":

                        mtrl_weld(mem1.main_mtrl(), [hss1, ], weld_size, hss_weld_type, weld_inter_length, weld_inter_spa, weld_stagger)

                       

            if not yes_or_no('Add more HSS fill material?'):

                break

    except:

        Warning(formatExceptionInfo())

    #### END run_script() #####################################################################################################

if __name__ == '__main__':

    try:

        run_script()

    finally:

        del run_script