-
Notifications
You must be signed in to change notification settings - Fork 4
Initializing Pathfinding Visualizer Axes to Axes of State Space #791
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
a00f086
63ab1cf
ad997b3
3b01a0f
e371da2
559fb1f
4289947
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -714,7 +714,11 @@ def add_arrow(origin, dx, dy, color, title, mag): | |
|
|
||
|
|
||
| def compute_and_add_state_space( | ||
| boat_xy_km: Tuple[float, float], goal_xy_km: Tuple[float, float], fig: go.Figure | ||
| boat_xy_km: Tuple[float, float], | ||
| goal_xy_km: Tuple[float, float], | ||
| fig: go.Figure, | ||
| zoom_needed: bool, | ||
| last_range: Optional[Dict[str, List[float]]] | ||
| ): | ||
| """ | ||
| Build the visualization state-space overlay around the boat and goal. Then, add the built | ||
|
|
@@ -749,6 +753,21 @@ def compute_and_add_state_space( | |
| layer="below", | ||
| ) | ||
|
|
||
| if not zoom_needed and last_range is not None: | ||
| fig.update_layout( | ||
| xaxis=dict(range=last_range["x"], | ||
| autorange=False), | ||
| yaxis=dict(range=last_range["y"], | ||
| autorange=False), | ||
| ) | ||
| else: | ||
| fig.update_layout( | ||
| xaxis=dict(range=[x_min, x_max], | ||
| autorange=False), | ||
| yaxis=dict(range=[y_min, y_max], | ||
| autorange=False), | ||
| ) | ||
|
|
||
|
|
||
| def add_goal_change_popup(fig: go.Figure, message: Optional[str]) -> None: | ||
| """ | ||
|
|
@@ -796,7 +815,9 @@ def apply_layout(fig: go.Figure) -> None: | |
|
|
||
|
|
||
| def build_figure( | ||
| state: VisualizerState, last_goal_xy_km: Optional[Tuple[float, float]] | ||
| state: VisualizerState, | ||
MilcToast marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| last_goal_xy_km: Optional[Tuple[float, float]], | ||
| last_range: Optional[Dict[str, List[float]]], | ||
| ) -> Tuple[go.Figure, Tuple[float, float]]: | ||
| """ | ||
| Builds and renders the complete path planning visualization figure. | ||
|
|
@@ -877,7 +898,8 @@ def build_figure( | |
| fig.add_annotation(annotation) | ||
|
|
||
| # Computing State space overlay and adding it to the plot | ||
| compute_and_add_state_space(boat_xy_km, goal_xy_km, fig) | ||
| zoom_needed = last_goal_xy_km is None | ||
| compute_and_add_state_space(boat_xy_km, goal_xy_km, fig, zoom_needed, last_range) | ||
| apply_layout(fig) | ||
|
||
| add_goal_change_popup(fig, goal_change.message) # Popup message for goal change | ||
| return fig, goal_change.new_goal_xy_rounded | ||
|
|
@@ -907,6 +929,7 @@ def dash_app(q: Queue): | |
| dcc.Graph(id="live-graph", style={"height": "90vh", "width": "100%"}), | ||
| dcc.Interval(id="interval-component", interval=UPDATE_INTERVAL_MS, n_intervals=0), | ||
| dcc.Store(id="goal-store", data=None), | ||
| dcc.Store(id="range-store", data=None), | ||
| html.Button( | ||
| "Reset the view to state space", | ||
| id="reset-button", | ||
|
|
@@ -928,53 +951,81 @@ def dash_app(q: Queue): | |
| @app.callback( | ||
| Output("live-graph", "figure"), | ||
| Output("goal-store", "data"), | ||
| Output("range-store", "data"), | ||
| Input("interval-component", "n_intervals"), | ||
| Input("live-graph", "relayoutData"), | ||
| Input("reset-button", "n_clicks"), | ||
| State("live-graph", "figure"), | ||
| State("goal-store", "data"), | ||
| State("range-store", "data"), | ||
| prevent_initial_call=True, | ||
| ) | ||
| def update_graph(_: int, __: int, current_figure, last_goal_xy_km: Optional[List[float]]): | ||
| def update_graph(_: int, | ||
| relayoutData, | ||
| __: int, | ||
| current_figure, | ||
| last_goal_xy_km: Optional[List[float]], | ||
| stored_range): | ||
| """ | ||
| Dash callback: handles both interval updates and reset button clicks. | ||
| Uses callback_context to determine which input triggered the update. | ||
|
|
||
| Args: | ||
| _: Dash interval tick (unused). | ||
| relayoutData: Data relayed from Plotly when user pans, zooms in/out, autoscales | ||
| __: Reset button n_clicks (unused). | ||
| current_figure: Current figure state from live-graph. | ||
| last_goal_xy_km: Previously stored goal as [x, y] or None on first run. | ||
| stored_range: Previously stored range as {"x": [xmin, xmax], "y": [ymin, ymax]} | ||
| or None on first run | ||
|
|
||
| Returns: | ||
| (fig, new_goal_as_list): | ||
| (fig, new_goal_as_list, last_range): | ||
| - fig: The updated Plotly figure | ||
| - new_goal_as_list: [x, y] for storage in dcc.Store (JSON serializable) | ||
| - last_range: [x-range, y-range] for storage in dcc.Store (JSON serializable) | ||
|
|
||
| """ | ||
| global queue | ||
|
|
||
| if relayoutData and "xaxis.range[0]" in relayoutData: | ||
| last_range = { | ||
| "x": [ | ||
| relayoutData["xaxis.range[0]"], | ||
| relayoutData["xaxis.range[1]"], | ||
| ], | ||
| "y": [ | ||
| relayoutData["yaxis.range[0]"], | ||
| relayoutData["yaxis.range[1]"], | ||
| ], | ||
| } | ||
MilcToast marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| else: | ||
| last_range = stored_range | ||
|
|
||
| ctx = dash.callback_context | ||
|
|
||
| triggered_id = ctx.triggered[0]["prop_id"].split(".")[0] if ctx.triggered else None | ||
|
|
||
| if triggered_id == "reset-button": | ||
| if current_figure is None: | ||
| return dash.no_update, dash.no_update | ||
| return dash.no_update, dash.no_update, dash.no_update | ||
|
|
||
| fig = go.Figure(current_figure) | ||
| fig.update_layout( | ||
| xaxis=dict(range=DEFAULT_PLOT_RANGE, autorange=False), | ||
| yaxis=dict(range=DEFAULT_PLOT_RANGE, autorange=False), | ||
| uirevision="reset", | ||
| ) | ||
MilcToast marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return fig, last_goal_xy_km | ||
| return fig, last_goal_xy_km, last_range | ||
|
|
||
| # Interval update (default behavior) | ||
| if queue is None or queue.empty(): | ||
| return dash.no_update, dash.no_update | ||
| return dash.no_update, dash.no_update, dash.no_update | ||
MilcToast marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| state = queue.get() # type: ignore | ||
| last_goal_tuple = ( | ||
| cs.XY(last_goal_xy_km[0], last_goal_xy_km[1]) if last_goal_xy_km is not None else None | ||
| ) | ||
|
|
||
| fig, new_goal_xy = build_figure(state, last_goal_tuple) | ||
| return fig, [new_goal_xy[0], new_goal_xy[1]] | ||
| fig, new_goal_xy = build_figure(state, last_goal_tuple, last_range) | ||
| return fig, [new_goal_xy[0], new_goal_xy[1]], last_range | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should make a note that this function changes the
vs