import streamlit as st import numpy as np from bokeh.plotting import figure from bokeh.models import ColumnDataSource from bokeh.events import Tap # Constants WIDTH, HEIGHT = 600, 600 ELASTICITY = 0.3 DAMPING = 0.7 # Create touch points num_points = 20 x = np.linspace(50, WIDTH-50, num_points) y = np.linspace(50, HEIGHT-50, num_points) xx, yy = np.meshgrid(x, y) touch_points = np.column_stack((xx.ravel(), yy.ravel())) original_points = touch_points.copy() velocities = np.zeros_like(touch_points, dtype=np.bool_) source = ColumnDataSource(data=dict(x=touch_points[:, 0], y=touch_points[:, 1])) # Set up the Bokeh plot p = figure(width=WIDTH, height=HEIGHT, tools="", toolbar_location=None) p.circle('x', 'y', size=5, color="navy", alpha=0.5, source=source) # Streamlit app st.title("Artificial Touch Simulation") # Create a Streamlit container for the Bokeh plot chart = st.bokeh_chart(p) def update_points(): global touch_points, velocities # Apply spring force force = (original_points - touch_points) * ELASTICITY # Update velocity velocities += force # Apply damping velocities *= DAMPING # Update position touch_points += velocities # Update Bokeh data source source.data = dict(x=touch_points[:, 0], y=touch_points[:, 1]) def on_tap(event): global touch_points, velocities x, y = event.x, event.y distances = np.sqrt(np.sum((touch_points - [x, y])**2, axis=1)) affected = distances < 30 force = (touch_points[affected] - [x, y]) / distances[affected, np.newaxis] velocities[affected] -= force * 10 st.write(f"Touch at ({x:.2f}, {y:.2f})") update_points() p.on_event(Tap, on_tap) while True: update_points() chart.bokeh_chart(p) st.experimental_rerun()