solution_061.py

#! /usr/bin/python3
# ===================================================================
# demonstrate translation/rotation/fold of a graphics object made
# of line segments and points
# ===================================================================

import numpy as np

import user_interface as ui
import draw_xy_axes as ax
import transformation_matrix as tm
import coordinate_conversion as cc

from two_vector_angle import two_vector_angle

from graphics import *

# ---- object (line segments)

segs = [ ( 200.0, 200.0, 200.0,-200.0),
         ( 200.0,-200.0,-200.0,-200.0),
         (-200.0,-200.0,-200.0,  50.0),
         (-200.0,  50.0,-200.0, 200.0),
         (-200.0, 200.0,-100.0, 200.0),
         (-100.0, 200.0, 200.0, 200.0), ]

# ---- fold line (line segment)

fln = (-200.0,50.0,-100.0,200.0)

# -------------------------------------------------------------------
# ---- draw line segment
# -------------------------------------------------------------------

def draw_line_segment(win,ul,x1,y1,x2,y2,ends=True,
                      color1='black',color2='black'):
    wx1,wy1 = cc.center_to_win_coords(x1,y1,win.width,win.height)
    wx2,wy2 = cc.center_to_win_coords(x2,y2,win.width,win.height)
    l = Line(Point(wx1,wy1),Point(wx2,wy2))
    l.setWidth(2)
    l.setFill(color1)
    l.draw(win)
    ul.append(l)
    if ends:
        draw_point(win,ul,x1,y1,color2)
        draw_point(win,ul,x2,y2,color2)

# -------------------------------------------------------------------
# ---- draw point (small circle)
# -------------------------------------------------------------------

def draw_point(win,ul,x,y,color='red'):
    wx,wy = cc.center_to_win_coords(x,y,win.width,win.height)
    p = Circle(Point(wx,wy),4)
    p.setFill(color)
    p.draw(win)
    ul.append(p)

# -------------------------------------------------------------------
# ---- draw object (made of line segments)
# ---- segs  object line segments
# ---- fln   fold line segment
# ---- cnt   draw fold line center point
# -------------------------------------------------------------------

def draw_object(win,segs,fln,cnt=True):

    # ---- undraw list

    ul = []

    # ---- draw object

    for s in segs:
        draw_line_segment(win,ul,s[0],s[1],s[2],s[3])

    # ---- draw fold line

    if fln is not None:
        draw_line_segment(win,ul,fln[0],fln[1],fln[2],fln[3],
                          True,'red','white')

        # ---- draw fold line center point

        if cnt:
            x = (fln[0]+fln[2])/2
            y = (fln[1]+fln[3])/2
            draw_point(win,ul,x,y,'white')

    return ul

# -------------------------------------------------------------------
# ---- rotate/translate a list of line segments
# -------------------------------------------------------------------

def translate_rotate_segs(x,y,ang,segs_old=None):

    segs_new = []

    for seg in segs_old:

        seg_new = translate_rotate_seg(x,y,ang,seg)

        segs_new.append(seg_new)

    return segs_new    

# -------------------------------------------------------------------
# ---- rotate/translate a line segment
# -------------------------------------------------------------------

def translate_rotate_seg(x,y,ang,seg):

    # ---- create a transformation matrix to:
    # ---- 1. move fold line center to the origin (0,0)
    # ---- 2. rotate fold line until vertical

    t1 = tm.get_translation_matrix_2d(x,y)
    tx = t1

    if ang is not None:
       t2 = tm.get_z_rotation_matrix_2d(ang)
       tx = t2 @ t1

    xy = [seg[0],seg[1],1]
    xy1 = tx @ xy

    xy = [seg[2],seg[3],1]
    xy2 = tx @ xy

    return (xy1[0],xy1[1],xy2[0],xy2[1])

# -------------------------------------------------------------------
# ---- main
# -------------------------------------------------------------------

if __name__ == '__main__':

    win_width  = 801
    win_height = 801

    # ---- create window

    win = GraphWin("Test Fold",win_width,win_height)
    win.setBackground("white")

    # ---- draw axes

    ax.draw_xy_axes(win,True)

    # ---- draw the object, etc.

    ul = draw_object(win,segs,fln)

    # ---- calculate the middle of the fold line

    x_fln = (fln[0]+fln[2])/2
    y_fln = (fln[1]+fln[3])/2

# ---- make the fold line points vector "up"
# ---- (this will simplify the fold angle calculation. the Y axes
# ---- points "up" and the fold line vector also points up.

    dx = fln[2] - fln[0]
    dy = fln[3] - fln[1]

    # ---- create a fold line / Y axis rotation angle
    # ---- (used to rotate the fold line onto the Y axis)

    v1 = [0.0,10.0]            # Y axis
    v2 = [dx,dy]               # fold line

    print(f'vector 1 = {v1}')
    print(f'vector 2 = {v2}')

    rot_rad = two_vector_angle(v1,v2) # rotation angle (rad)
    rot_deg = np.rad2deg(rot_rad)     # rotation angle (deg)

    print(f'rotation angle = {rot_deg:.3f} degrees')

    # ---- rotate/translate the line segments, etc.
    # ---- put the center of the fold line at the origin (0,0)

    print()
    print('translate/rotate object, etc.')
    ui.pause()

    for o in ul:
        o.undraw()

    segs_new = \
        translate_rotate_segs(-x_fln,-y_fln,rot_deg,segs)
    fln_new = \
        translate_rotate_seg(-x_fln,-y_fln,rot_deg,fln)

    ul = draw_object(win,segs_new,fln_new)

    # ---- fold the new object

    print()
    print('fold the new object, etc.')
    ui.pause()

    ##????????????????????????????????????????????????
    ## much better code required here

    segs_fold = []

    for s in segs_new:
        if s[0] < 0:           # segment point0 X coord
            x1 = -s[0]
        else:
            x1 = s[0]
        if s[2] < 0:           # segment point1 X coord
            x2 = -s[2]
        else:
            x2 = s[2]

        segs_fold.append((x1,s[1],x2,s[3]))

    # ---- restore the object, etc.
    # ---- put them back where they came from

    print()
    print('restore the original object, etc.')
    ui.pause()

    for o in ul:
        o.undraw()

    segs_restore = \
        translate_rotate_segs(x_fln,y_fln,-rot_deg,segs_fold)
    fln_restore = \
        translate_rotate_seg(x_fln,y_fln,-rot_deg,fln_new)

    ul = draw_object(win,segs_restore,None)

    draw_line_segment(win,ul,fln_restore[0],
                             fln_restore[1],
                             fln_restore[2],
                             fln_restore[3],False)

    # ---- end program

    print()
    print('exit the program')

    ui.pause()
    win.close()
    print()