|
| 1 | +""" |
| 2 | +Interactive equilibrium simulation using matplotlib. |
| 3 | +
|
| 4 | +This script provides a visual demonstration of the `Equilibrium` class. |
| 5 | +It creates an interactive 2D plot where the user can move the mouse to |
| 6 | +simulate the barycenter position, and the system evaluates whether the |
| 7 | +barycenter is within the equilibrium ellipse defined by two feet. |
| 8 | +
|
| 9 | +The ellipse is dynamically updated in position, orientation, and color: |
| 10 | +- Green if the barycenter is within the ellipse. |
| 11 | +- Red if the barycenter is outside. |
| 12 | +
|
| 13 | +The equilibrium value (in [0, 1]) is displayed in real-time. |
| 14 | +
|
| 15 | +Examples |
| 16 | +-------- |
| 17 | +Run the script: |
| 18 | +
|
| 19 | + $ python demo_equilibrium.py |
| 20 | +
|
| 21 | +Then move the mouse cursor inside the figure window to simulate barycenter |
| 22 | +movements. |
| 23 | +
|
| 24 | +Notes |
| 25 | +----- |
| 26 | +- Requires `matplotlib` for visualization. |
| 27 | +- Uses `numpy` for vector operations. |
| 28 | +""" |
| 29 | + |
| 30 | +import numpy as np |
| 31 | +import matplotlib.pyplot as plt |
| 32 | +from matplotlib.patches import Ellipse |
| 33 | +import sys, os |
| 34 | +if os.getcwd() not in sys.path: |
| 35 | + sys.path.append(os.getcwd()) |
| 36 | +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) |
| 37 | +from core.equilibrium import Equilibrium |
| 38 | + |
| 39 | +# --- Setup test parameters --- |
| 40 | +left_foot = np.array([120, 200, 0]) |
| 41 | +"""numpy.ndarray: 3D coordinates (x, y, z) of the left foot in millimeters.""" |
| 42 | + |
| 43 | +right_foot = np.array([800, 600, 0]) |
| 44 | +"""numpy.ndarray: 3D coordinates (x, y, z) of the right foot in millimeters.""" |
| 45 | + |
| 46 | +eq = Equilibrium(margin_mm=100, y_weight=0.5) |
| 47 | +"""Equilibrium: instance of the equilibrium evaluator.""" |
| 48 | + |
| 49 | +# --- Setup matplotlib figure --- |
| 50 | +fig, ax = plt.subplots() |
| 51 | +ax.set_xlim(-200, 1000) |
| 52 | +ax.set_ylim(-200, 800) |
| 53 | +ax.set_aspect('equal') |
| 54 | +ax.set_title("Equilibrium. Move the barycenter with mouse.") |
| 55 | + |
| 56 | +# Plot the two feet as blue points |
| 57 | +ax.plot(left_foot[0], left_foot[1], 'bo', markersize=8) |
| 58 | +ax.plot(right_foot[0], right_foot[1], 'bo', markersize=8) |
| 59 | + |
| 60 | +# Ellipse ROI (initial placeholder) |
| 61 | +roi_ellipse = Ellipse((0, 0), 0, 0, |
| 62 | + fill=True, facecolor='green', alpha=0.2, |
| 63 | + edgecolor='green', linewidth=2) |
| 64 | +ax.add_patch(roi_ellipse) |
| 65 | + |
| 66 | +# Red point for barycenter |
| 67 | +baricentro_plot, = ax.plot([], [], 'ro', markersize=8) |
| 68 | + |
| 69 | +# Text showing equilibrium value |
| 70 | +eq_text = ax.text(0.02, 1.02, "", transform=ax.transAxes, fontsize=12) |
| 71 | + |
| 72 | +def on_move(event): |
| 73 | + """ |
| 74 | + Handle mouse movement and update equilibrium visualization. |
| 75 | +
|
| 76 | + Parameters |
| 77 | + ---------- |
| 78 | + event : matplotlib.backend_bases.MouseEvent |
| 79 | + The mouse event containing the current cursor position. |
| 80 | + Only `event.xdata` and `event.ydata` are used. |
| 81 | +
|
| 82 | + Behavior |
| 83 | + -------- |
| 84 | + - Computes equilibrium value and ellipse angle based on the |
| 85 | + simulated barycenter. |
| 86 | + - Updates ellipse position, size, orientation, and color. |
| 87 | + - Updates barycenter position on the plot. |
| 88 | + - Updates equilibrium value text. |
| 89 | + """ |
| 90 | + if not event.inaxes: |
| 91 | + return |
| 92 | + |
| 93 | + baricentro = np.array([event.xdata, event.ydata, 0]) |
| 94 | + |
| 95 | + value, angle = eq(left_foot, right_foot, baricentro) |
| 96 | + |
| 97 | + ps = np.array(left_foot)[:2] |
| 98 | + pd = np.array(right_foot)[:2] |
| 99 | + min_xy = np.minimum(ps, pd) - eq.margin |
| 100 | + max_xy = np.maximum(ps, pd) + eq.margin |
| 101 | + center = (min_xy + max_xy) / 2 |
| 102 | + half_sizes = (max_xy - min_xy) / 2 |
| 103 | + a = half_sizes[0] |
| 104 | + b = half_sizes[1] * eq.y_weight |
| 105 | + |
| 106 | + roi_ellipse.set_center(center) |
| 107 | + roi_ellipse.width = 2 * a |
| 108 | + roi_ellipse.height = 2 * b |
| 109 | + roi_ellipse.angle = angle |
| 110 | + roi_ellipse.set_facecolor('green' if value > 0 else 'red') |
| 111 | + roi_ellipse.set_edgecolor('green' if value > 0 else 'red') |
| 112 | + |
| 113 | + baricentro_plot.set_data(baricentro[0], baricentro[1]) |
| 114 | + |
| 115 | + eq_text.set_text(f"Equilibrium value = {value:.2f}") |
| 116 | + |
| 117 | + fig.canvas.draw_idle() |
| 118 | + |
| 119 | +# Connect mouse motion event |
| 120 | +fig.canvas.mpl_connect('motion_notify_event', on_move) |
| 121 | + |
| 122 | +# Run interactive visualization |
| 123 | +plt.show() |
0 commit comments