Game AI & Unity/L-system algorithm

[Plants and Python][prac9] 함수와 프랙탈 3

bay07 2024. 3. 29. 10:23

#6 branching_turtle_to_coords

#6
def branching_turtle_to_coords(turtle_program, turn_amount=45):
    saved_states = list()
    state = (0, 0, 90)
    yield (0, 0)

    for command in turtle_program:
        x, y, angle = state

        if command.lower() in 'abcdefghijklmnopqrstuvwxyz':
            state = (x - cos(angle * DEGREES_TO_RADIANS),
                     y + sin(angle * DEGREES_TO_RADIANS),
                     angle)
            if command.islower():
                yield (float('nan'), float('nan'))
            yield (state[0], state[1])
            
        elif command == '+':
            state = (x, y, angle + turn_amount)
        elif command == '-':
            state = (x, y, angle - turn_amount)
        elif command == '[':
            saved_states.append(state)
        elif command == ']':
            state = saved_states.pop()
            yield (float('nan'), float('nan'))
            x, y, _ = state
            yield (x, y)

 

branching_turtle_to_coords(turtle_program, turn_amount=45) 주어진 거북이 프로그램(turtle_program)을 해석하여
좌표로 변환한다
turtle_program : 거북이 프로그램을 나타내는 문자열, 거북이의 이동 및 회전 명령을 포함
turn_amount : 거북이가 회전할 각도, 기본값은 45도

saved_states = list() 이동 전의 거북이 상태를 저장하는 빈 리스트
state = (0, 0, 90) 초기 거북이 상태를 설정
초기 위치는 (0, 0)이며, 초기 각도는 90도(위쪽 방향)
yield (0, 0) 초기 위치를 좌표로 출력
x, y, angle = state 현재 거북이의 위치와 각도를 state에서 가져온다
if command.lower() in 'abcdefghijklmnopqrstuvwxyz' 현재 명령이 이동 명령인 경우
소문자 알파벳은 거북이를 이동시키는 명령을 나타낸다
state = (x - cos(angle * DEGREES_TO_RADIANS), y + sin(angle * DEGREES_TO_RADIANS), angle) 거북이의 새로운 위치를 계산
이동은 현재 각도에 따라 코사인과 사인 함수를 사용하여 계산된다
if command.islower() 이동 명령이 소문자인 경우, 선을 그리는 도중에 선을 중단하도록 <gap>을 출력한다. 프랙탈을 그릴 때 끊어진 선을 나타냄
yield (state[0], state[1]) 이동에 따른 새로운 위치를 좌표로 출력
elif command == '+': 현재 명령이 시계 방향으로 회전하는 명령인 경우
state = (x, y, angle + turn_amount) 거북이의 새로운 각도를 계산하여 회전한다
elif command == '-': 반시계 방향으로 회전하는 명령인 경우
state = (x, y, angle - turn_amount) 거북이의 새로운 각도를 계산하여 회전한다
elif command == '[': 현재 명령이 상태를 저장하는 명령인 경우
saved_states.append(state) 현재 거북이의 상태를 저장한다
elif command == ']' 현재 명령이 이전 상태로 돌아가는 명령인 경우
state = saved_states.pop() 이전에 저장된 상태로 거북이의 상태를 복원한다
yield (float('nan'), float('nan')) 선을 그리는 중간에 끊어진 부분을 나타내기 위해 <gap>을 출력한다
x, y, _ = state 이전 상태에서의 x, y 좌표와 각도를 가져온다
_는 사용되지 않는 변수
각도를 가져올 필요가 없기 때문에 사용되었다
yield (x, y) 전 상태로 돌아간 거북이의 위치를 출력
[ 명령 이후의 명령을 처리하기 위해 이전 위치로 거북이를 이동시키는 것

 


 

▷ data code 1

더보기
axiom = 'F-F-F-F'
production_rules = {'F':'F+FF-FF-F-F+F+FF-F-F+F+FF+FF-F'}
iterations = 2
angle = 90

 

axiom = '-F'
production_rules = {'F':'F+F-F-F+F'}
iterations = 4
angle = 90

 

axiom = 'F+F+F+F'
production_rules = {'F':'F+f-FF+F+FF+Ff+FF-f+FF-F-FF-Ff-FFF', 'f':'ffffff'}
iterations = 2
angle = 90

 

axiom = 'F-F-F-F'
production_rules = {'F':'FF-F-F-F-F-F+F'}
iterations = 4
angle = 90

 

axiom = 'F-F-F-F'
production_rules = {'F':'FF-F-F-F-FF'}
iterations = 4
angle = 90

 

axiom = 'F-F-F-F'
production_rules = {'F':'FF-F+F-F-FF'}
iterations = 3
angle = 90

 

axiom = 'F-F-F-F'
production_rules = {'F':'FF-F--F-F'}
iterations = 4
angle = 90

 

axiom = 'F-F-F-F'
production_rules = {'F':'F-FF--F-F'}
iterations = 5
angle = 90

 

axiom = 'F-F-F-F'
production_rules = {'F':'F-F+F-F-F'}
iterations = 4
angle = 90

 

 

▷ data code 2

더보기
axiom = 'L'
production_rules = {'L':'L+R+', 'R':'-L-R'}
iterations = 10
angle = 90

 

axiom = 'R'
production_rules = {'L':'R+L+R', 'R':'L-R-L'}
iterations = 6
angle = 60

 

axiom = 'L'
production_rules = {'L':'L+R++R-L--LL-R+', 'R':'-L+RR++R+L--L-R'}
iterations = 4
angle = 60

 

axiom = '-R'
production_rules = {'L':'LL-R-R+L+L-R-RL+R+LLR-L+R+LL+R-LR-R-L+L+RR-', 
              'R':'+LL-R-R+L+LR+L-RR-L-R+LRR-L-RL+L+R-R-L+L+RR'}
iterations = 2
angle = 90

 

 

▷ 전체 코드

더보기
import matplotlib.pyplot as plt
%matplotlib inline
nan = float('nan')

#1 
def plot_coords(coords, lw=0.5, bare_plot=True):
    if bare_plot:
        # Turns off the axis markers.
        plt.axis('off')
    # Ensures equal aspect ratio.
    plt.axes().set_aspect('equal', 'datalim')
    # Converts a list of coordinates into
    # lists of X and Y values, respectively.
    X, Y = zip(*coords)
    # Draws the plot.
    plt.plot(X, Y, lw=lw, c='k')



#2
from math import pi, sin, cos
DEGREES_TO_RADIANS = pi / 180

def turtle_to_coords(turtle_program, turn_amount=45):
    # The state variable tracks the current location and angle of the turtle.
    # The turtle starts at (0, 0) facing up (90 degrees).
    state = (0.0, 0.0, 90.0)

    # Throughout the turtle's journey, we "yield" its location. These coordinate
    # pairs become the path that plot_coords draws.
    yield (0.0, 0.0)

    # Loop over the program, one character at a time.
    for command in turtle_program:
        x, y, angle = state

        # Move turtle forward
        if command in 'Ff':
            state = (x - cos(angle * DEGREES_TO_RADIANS),
                     y + sin(angle * DEGREES_TO_RADIANS),
                     angle)

            if command == 'f':
                # Insert a break in the path so that
                # this line segment isn't drawn.
                yield (float('nan'), float('nan'))

            yield (state[0], state[1])

        # Turn turtle clockwise without moving
        elif command == '+':
            state = (x, y, angle + turn_amount)

        # Turn turtle counter-clockwise without moving
        elif command == '-':
            state = (x, y, angle - turn_amount)

        # Note: We silently ignore unknown commands


#3
from math import isnan
def print_coords(coords):
    for (x, y) in coords:
        if isnan(x):
            print('<gap>')
        else:
            print('({:.2f}, {:.2f})'.format(x, y))

#4 
def transform_sequence(sequence, transformations):
    return ''.join(transformations.get(c, c) for c in sequence)

#5
def transform_multiple(sequence, transformations, iterations):
    for _ in range(iterations):
        sequence = transform_sequence(sequence, transformations)
    return sequence

#6
def branching_turtle_to_coords(turtle_program, turn_amount=45):
    saved_states = list()
    state = (0, 0, 90)
    yield (0, 0)

    for command in turtle_program:
        x, y, angle = state

        # Move forward (matches a-j and A-J)

        if command.lower() in 'abcdefghijklmnopqrstuvwxyz':
            state = (x - cos(angle * DEGREES_TO_RADIANS),
                     y + sin(angle * DEGREES_TO_RADIANS),
                     angle)

            # Add a break in the line if command matches a-j
            if command.islower():
                yield (float('nan'), float('nan'))

            yield (state[0], state[1])

        # Turn clockwise
        elif command == '+':
            state = (x, y, angle + turn_amount)

        # Turn counterclockwise
        elif command == '-':
            state = (x, y, angle - turn_amount)

        # Remember current state
        elif command == '[':
            saved_states.append(state)

        # Return to previous state
        elif command == ']':
            state = saved_states.pop()
            yield (float('nan'), float('nan'))
            x, y, _ = state
            yield (x, y)

        # Note: We silently ignore unknown commands
#7
def l_plot(axiom, transformations, iterations=0, angle=45, lw=0.5):
    turtle_program = transform_multiple(axiom, transformations, iterations)
    coords = branching_turtle_to_coords(turtle_program, angle)
    plot_coords(coords, lw=lw, bare_plot=True)


# 예시
axiom = 'F-F-F-F'
production_rules = {'F':'F+FF-FF-F-F+F+FF-F-F+F+FF+FF-F'}
iterations = 2
angle = 90

# 함수 불러오기
# Plot the L-system fractal
l_plot(axiom, production_rules, iterations, angle)

* 참고 
https://nbviewer.org/github/DanChitwood/PlantsAndPython/blob/master/PlantsAndPython6_STUDENT_Functions_and_Fractals.ipynb
https://nbviewer.org/github/DanChitwood/PlantsAndPython/tree/master/