File size: 6,985 Bytes
5f59ba1
 
 
 
 
fef59a7
5f59ba1
 
 
 
 
fef59a7
5f59ba1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
"""
Streamlit application
"""
from loguru import logger
import mpld3
import streamlit as st
import streamlit.components.v1 as components
from streamlit.errors import StreamlitAPIException
import yaml
import lsystc as ls
import specific_values as sv

st.set_page_config(page_title="Curves with L-systems", page_icon="🖼️", layout="wide")


@st.cache_data
def load_result(axiom, mult_axiom, rules, rotation_angle, starting_angle, skipped, nb_iterations, coeff):
    """
    Return the result of the L-system with the specified parameters

    :param axiom:
    :param mult_axiom:
    :param rules:
    :param rotation_angle:
    :param starting_angle:
    :param skipped:
    :param nb_iterations:
    :param coeff:
    :return: the result of the "L-System rendering"
    """
    try:
        config = ls.Config(skipped=skipped)
        rules = rules.strip("; ")
        rules_list = []

        if rules:
            for item in rules.split(";"):
                if ':' in item:
                    splits = item.split(":")
                    if len(splits) == 2 and splits[0].strip():
                        left = splits[0].strip()
                        right = splits[1].strip()
                        rules_list.append((left, right))
                    else:
                        raise ValueError(f"Every rule must be correctly written ( {item} )")
                else:
                    raise ValueError(f"Every non empty rule must include a column character ( {item} ) ")

        rls = ls.Lsystc(config, axiom * mult_axiom, rules_list, nbiter=nb_iterations, verbose=sv.verbose)
        rls.turtle(step=sv.step, angle=rotation_angle, angleinit=starting_angle, coeff=coeff,
                   color_length=sv.color_length, color_map=sv.color_map)

        result = rls.render(sv.renderer, save_files=sv.save_files, show_more=sv.show_more, show_3d=sv.show_3d,
                            return_type=sv.return_type)
    except ValueError as ex:
        st.warning(f"Please verify your parameters - {ex}")
        st.stop()
    except Exception as ex:
        st.warning("Please verify your parameters")
        if sv.verbose:
            logger.exception(f"Something went wrong : {ex}")
        st.stop()
    else:
        return result


def write_specific(content):
    """
    Write streamlit content

    :param content: streamlit content
    :return: None
    """
    try:
        st.write(content)
    except StreamlitAPIException as exc:
        # Currently : streamlit.errors.StreamlitAPIException: `_repr_html_()` is not a valid Streamlit command.
        if sv.verbose:
            logger.error(exc)


def on_change_selection():
    """
    Change the parameters when the starting example is changed

    :return: None
    """
    current_selection = st.session_state.my_selection
    axiom, mult_axiom, rules, rotation_angle, starting_angle, nb_iter, skipped, coeff = examples_data[current_selection]
    st.session_state.my_axiom = axiom
    st.session_state.my_mult_axiom = mult_axiom
    st.session_state.my_rules = rules
    st.session_state.my_rotation_angle = rotation_angle
    st.session_state.my_starting_angle = starting_angle
    st.session_state.my_nb_iter = nb_iter
    st.session_state.my_skipped = skipped
    st.session_state.my_coeff = coeff
    sv.redraw_auto = True


with open("curves_parameters.yaml", 'r', encoding='utf8') as f:
    curves_parameters = yaml.safe_load(f)

EXAMPLES_DEFAULT = 0
examples_names = []
examples_data = {}
for c_name, c_params in curves_parameters.items():
    examples_names.append(c_name)
    c_axiom = c_params.get('axiom', '')
    c_axiom_multiplier = c_params.get('axiom_multiplier', 1)
    c_rules = c_params.get('rules', '')
    c_rotation_angle = c_params.get('rotation_angle', 90.0)
    c_starting_angle = c_params.get('starting_angle', 0)
    c_nb_iter = c_params.get('nb_iter', 1)
    c_skipped = c_params.get('skipped', '')
    c_coeff = c_params.get('coeff', 1.0)
    examples_data[c_name] = (c_axiom, c_axiom_multiplier, c_rules,
                             c_rotation_angle, c_starting_angle, c_nb_iter, c_skipped, c_coeff)


EXAMPLES_DEFAULT_name = examples_names[EXAMPLES_DEFAULT]
def_axiom, def_mult_axiom, def_rules, def_rotation_angle, def_starting_angle, def_nb_iter, def_skipped, def_coeff = \
    examples_data[EXAMPLES_DEFAULT_name]

with st.sidebar:
    MD_INTRO = """
    You have the flexibility to select a starting example and to change the parameters :sunglasses:
    """

    st.markdown(MD_INTRO)

    input_selection = st.selectbox('Starting example', examples_names,
                                   index=EXAMPLES_DEFAULT, on_change=on_change_selection, key="my_selection")

    input_axiom = st.text_input('Starting axiom', def_axiom, key="my_axiom")
    input_mult_axiom = st.number_input('Multiplier for axiom', value=def_mult_axiom, min_value=1, key="my_mult_axiom")

    input_rules = st.text_input('Rules', def_rules, help="Example for 2 rules ->   A:  ABC ;  B:   CAB ; ",
                                key="my_rules")
    input_rotation_angle = st.number_input('Angle of rotation',
                                           value=def_rotation_angle, min_value=1.0, max_value=360.0, step=1.0,
                                           format='%f', key="my_rotation_angle")
    input_starting_angle = st.number_input('Starting angle',
                                           value=def_starting_angle, min_value=0, max_value=360,
                                           format='%d', key="my_starting_angle")

    input_skipped = st.text_input('Skipped characters', def_skipped, key="my_skipped")

    input_coeff = st.number_input('Coefficient',
                                  value=def_coeff, format='%f', key="my_coeff")

    input_nb_iter = st.number_input('Number of iterations',
                                    value=def_nb_iter, min_value=1, max_value=15, format='%d', key="my_nb_iter")

st.markdown("# Curves with L-systems")

st.markdown("""**Click on "Draw" to display the curve**""")

if st.button('Draw') or sv.redraw_auto or 'start' not in st.session_state:
    sv.redraw_auto = False
    st.session_state['start'] = True
    res = load_result(input_axiom, input_mult_axiom, input_rules, input_rotation_angle, input_starting_angle,
                      input_skipped, input_nb_iter, input_coeff)

    if sv.return_type == 'image':
        write_specific(st.image(res, caption='Generated image'))
    else:
        if sv.renderer == 'matplot':
            # Pyplot figure
            # write_specific(st.pyplot(res))
            fig_html = mpld3.fig_to_html(res)
            components.html(fig_html, height=600)
        elif sv.renderer == 'bokeh':
            st.bokeh_chart(res, use_container_width=True)
        elif sv.renderer == 'plotly':
            st.plotly_chart(res, use_container_width=True)

st.markdown("---")
st.markdown("More details on Lindenmayer systems here : [Wikipedia](https://en.wikipedia.org/wiki/L-system)")