curves / app.py
gdarid's picture
add the interface with curves
5f59ba1
"""
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)")