# Modify selected nodes to the same constant value -- contributed to Mecway Forum by @JohnM

# Script modified by cwharpe 8/15/22 to accept all (3) Cartesian axis directions 
# Script modified by cwharpe 8/22/22 to Align nodes by perpendicular projection to any 3D Line

cloud = mw.selected_nodes()
nn = len(cloud)
options = ["x", "y", "z","l"]
ErrFlag=0
if nn<3:
    mw.message("Need to PRE-Select (3) or more nodes before running script.")

choice = mw.input("ALIGN PRE-Selected Nodes to which Constant AXIS?:                     (X, Y, Z) or (L)ine (by user)").lower()
if choice == "":
    mw.message("No vector given!")
    ErrFlag=1
elif choice not in options:
    mw.message("X, Y, Z or L are the only valid choices")
    ErrFlag=1
else:

# Alignment to Cartesian Axis
    if choice =="x":
        def mod_node(nodes, value):
            for point in nodes:
                mw.set_node_x(point, value)
    if choice =="y":
        def mod_node(nodes, value):
            for point in nodes:
                mw.set_node_y(point, value)
    if choice =="z":
        def mod_node(nodes, value):
            for point in nodes:
                mw.set_node_z(point, value)

# Alignment to User Axis specified by two 3D points.
    if choice =="l":
        ErrFlag=1
        mw.message("Must PRE-Select Line EndPts. from Cloud Nodes using (2) CTRL Re-Pick's.  Last (2) nodes selected in buffer are assumed as Line EndPts.")

# Coordinates of Line AB
        A = mw.node(cloud[nn-2])     # Vector position of pt. "A" on line
        B = mw.node(cloud[nn-1])     # Vector position of pt. "B" on line
        x1 = A.x
        y1 = A.y
        z1 = A.z
        x2 = B.x
        y2 = B.y
        z2 = B.z
# Cycle through Point Cloud                        
        for point in cloud:
            C = mw.node(point)     # Vector position of general cloud pt."C"
            x3 = C.x
            y3 = C.y
            z3 = C.z                
#   Perpendicular-Point-on-Line From 3D point  -- per Internet.       
            alpha =((x3-x1)*(x2-x1)+(y3-y1)*(y2-y1)+(z3-z1)*(z2-z1))/((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)+(z2-z1)*(z2-z1))
            x4 = x1+alpha*(x2-x1)
            y4 = y1+alpha*(y2-y1)
            z4 = z1+alpha*(z2-z1)
#   Adjust Node Coordinates to their perpendicular projection onto User Line AB
            mw.set_node_x(point, x4)
            mw.set_node_y(point, y4)
            mw.set_node_z(point, z4)        
# END of Align to Line AB Section
    
                  # Get selected nodes Parallel to X,Y,or Z axis
if ErrFlag == 0:
    optunit = [1,2,3,4,5,6]
    unit = (mw.input("LENGTH UNIT:  Choose the No. (below) associated w/        length:  1) m   2) mm   3) um   4) nm   5) in   6) ft"))
    if unit == "" or int(unit) not in optunit:
        unit = (mw.input("Choice Out-of-Range:  Choose Again No. associated w/        length:  1) m   2) mm   3) um   4) nm   5) in   6) ft"))
        if unit == "" or int(unit) not in optunit:
            mw.message("Choice not among options:  Default unit = meters")
            unit = 1
    convert=[1,1.0e-3,1.0e-6,1.0e-9,.0254,.3048]
    value = mw.input("Input common coordinate value: ")
    mod_node(cloud, float(value)*(convert[int(unit)-1]))
