{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "62debcf2",
   "metadata": {},
   "source": [
    "This notebook is used for walking load calculation with atr_tools package."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8fe41372",
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "from pathlib import Path\n",
    "import ipywidgets as widgets\n",
    "from IPython.display import display\n",
    "\n",
    "# Add parent directory to sys.path to import atr_tools\n",
    "notebook_dir = Path.cwd()\n",
    "parent_dir = notebook_dir.parent\n",
    "if str(parent_dir) not in sys.path:\n",
    "    sys.path.append(str(parent_dir))\n",
    "\n",
    "from haskoning_atr_tools.human_induced_vibrations.walking_load import calculate_walking_load\n",
    "\n",
    "# Override matplotlib backend settings for Jupyter notebooks\n",
    "%matplotlib inline\n",
    "import matplotlib\n",
    "import matplotlib.pyplot as plt\n",
    "import warnings\n",
    "\n",
    "# Force matplotlib to use the inline backend\n",
    "matplotlib.use('module://matplotlib_inline.backend_inline')\n",
    "# Initialize matplotlib interactive mode \n",
    "plt.ioff()\n",
    "plt.ion()\n",
    "# Suppress the FigureCanvasAgg warning if it appears\n",
    "warnings.filterwarnings('ignore', 'FigureCanvasAgg is non-interactive')\n",
    "\n",
    "\n",
    "class WalkingLoadCalculator:\n",
    "    \"\"\"Walking Load Calculator class\"\"\"\n",
    "    \n",
    "    def __init__(self):\n",
    "        self.current_walking_load = None\n",
    "        self.current_figure = None\n",
    "        self.setup_widgets()\n",
    "        self.display_interface()\n",
    "    \n",
    "    def setup_widgets(self):\n",
    "        \"\"\"Create and configure all UI widgets\"\"\"\n",
    "        # Input widgets\n",
    "        self.step_frequency_input = widgets.FloatText(\n",
    "            value=2.0,\n",
    "            description='Step frequency (Hz):',\n",
    "            disabled=False,\n",
    "            layout=widgets.Layout(width='400px'),\n",
    "            style={'description_width': '150px'}\n",
    "        )\n",
    "        \n",
    "        self.body_mass_input = widgets.FloatText(\n",
    "            value=75.0,\n",
    "            description='Body mass (kg):',\n",
    "            disabled=False,\n",
    "            layout=widgets.Layout(width='400px'),\n",
    "            style={'description_width': '150px'}\n",
    "        )\n",
    "        \n",
    "        self.number_of_steps_input = widgets.IntText(\n",
    "            value=15,\n",
    "            description='Number of steps:',\n",
    "            disabled=False,\n",
    "            layout=widgets.Layout(width='400px'),\n",
    "            style={'description_width': '150px'}\n",
    "        )\n",
    "        \n",
    "        self.load_reduction_toggle = widgets.Checkbox(\n",
    "            value=False,\n",
    "            description='Enable load reduction',\n",
    "            disabled=False,\n",
    "            layout=widgets.Layout(width='400px'),\n",
    "            style={'description_width': '150px'}\n",
    "        )\n",
    "        \n",
    "        # Buttons\n",
    "        self.calculate_button = widgets.Button(\n",
    "            description='Calculate Walking Load',\n",
    "            disabled=False,\n",
    "            button_style='primary',\n",
    "            layout=widgets.Layout(width='400px', height='40px')\n",
    "        )\n",
    "        \n",
    "        self.plot_button = widgets.Button(\n",
    "            description='Plot Walking Load',\n",
    "            disabled=True,\n",
    "            button_style='success',\n",
    "            layout=widgets.Layout(width='400px', height='40px')\n",
    "        )\n",
    "        \n",
    "        self.save_plot_button = widgets.Button(\n",
    "            description='Save Plot',\n",
    "            disabled=True,\n",
    "            button_style='info',\n",
    "            layout=widgets.Layout(width='400px', height='40px')\n",
    "        )\n",
    "        \n",
    "        self.clear_button = widgets.Button(\n",
    "            description='Clear All & Reset',\n",
    "            disabled=False,\n",
    "            button_style='warning',\n",
    "            layout=widgets.Layout(width='400px', height='40px')\n",
    "        )\n",
    "        \n",
    "        # Output areas\n",
    "        self.output_area = widgets.Output()\n",
    "        self.plot_output_area = widgets.Output()\n",
    "        \n",
    "        # Connect button handlers\n",
    "        self.calculate_button.on_click(self.calculate_handler)\n",
    "        self.plot_button.on_click(self.plot_handler)\n",
    "        self.save_plot_button.on_click(self.save_plot_handler)\n",
    "        self.clear_button.on_click(self.clear_handler)\n",
    "    \n",
    "    def display_interface(self):\n",
    "        \"\"\"Display the complete UI\"\"\"\n",
    "        display(widgets.HTML(\"<h3>🚶Walking Load Calculator </h3>\"))\n",
    "        display(self.step_frequency_input)\n",
    "        display(self.body_mass_input)\n",
    "        display(self.number_of_steps_input)\n",
    "        display(self.load_reduction_toggle)\n",
    "        display(self.calculate_button)\n",
    "        display(self.plot_button)\n",
    "        display(self.save_plot_button)\n",
    "        display(self.clear_button)\n",
    "        display(self.output_area)\n",
    "        display(self.plot_output_area)\n",
    "    \n",
    "    def calculate_handler(self, button):\n",
    "        \"\"\"Handle the calculate button click\"\"\"\n",
    "        with self.plot_output_area:\n",
    "            self.plot_output_area.clear_output(wait=True)\n",
    "        \n",
    "        with self.output_area:\n",
    "            self.output_area.clear_output(wait=True)\n",
    "            \n",
    "            try:\n",
    "                # Get values from typed widgets\n",
    "                step_frequency = self.step_frequency_input.value\n",
    "                body_mass = self.body_mass_input.value\n",
    "                nr_steps = self.number_of_steps_input.value\n",
    "                load_reduction = self.load_reduction_toggle.value\n",
    "                \n",
    "                # Validation\n",
    "                if not (1.64 <= step_frequency <= 3.0):\n",
    "                    print(\"Error: Step frequency should be between 1.64 and 3.0 Hz\")\n",
    "                    return\n",
    "                if not (40 <= body_mass <= 125):\n",
    "                    print(\"Error: Body mass should be between 40 and 125 kg\")\n",
    "                    return\n",
    "                if nr_steps < 15:\n",
    "                    print(\"Error: Number of steps should be at least 15\")\n",
    "                    return\n",
    "                \n",
    "                # Calculate walking load\n",
    "                walking_load = calculate_walking_load(\n",
    "                    step_frequency=step_frequency,\n",
    "                    body_mass=body_mass,\n",
    "                    nr_steps=nr_steps,\n",
    "                    load_reduction=load_reduction\n",
    "                )\n",
    "                \n",
    "                # Store results\n",
    "                self.current_walking_load = walking_load\n",
    "                \n",
    "                print(f\"Walking load calculated successfully!\")\n",
    "                print(f\"Total walking time: {walking_load.total_walking_time:.3f} seconds\")\n",
    "                print(f\"Time interval between steps: {walking_load.interval:.3f} seconds\")\n",
    "                print(f\"Step load duration: {walking_load.step_load.step_load_duration:.3f} seconds\")\n",
    "                \n",
    "                # Update button states\n",
    "                self.plot_button.disabled = False\n",
    "                self.calculate_button.disabled = True\n",
    "                \n",
    "            except ValueError:\n",
    "                print(\"Input error: Please enter valid numbers for all fields\")\n",
    "            except Exception as e:\n",
    "                print(f\"Calculation error: {e}\")\n",
    "    \n",
    "    def plot_handler(self, button):\n",
    "        \"\"\"Handle the plot button click\"\"\"\n",
    "        if self.current_walking_load is None:\n",
    "            with self.output_area:\n",
    "                print(\"Error: Please calculate the walking load first!\")\n",
    "            return\n",
    "        \n",
    "        with self.plot_output_area:\n",
    "            self.plot_output_area.clear_output(wait=True)\n",
    "            \n",
    "            # Calculate and plot\n",
    "            fig, ax = plt.subplots(figsize=(10, 6))\n",
    "            ax.plot(self.current_walking_load.time_domain, self.current_walking_load.step_walking_loads, 'b-', linewidth=2)\n",
    "            ax.set_xlabel('Time [s]')\n",
    "            ax.set_ylabel('Load [kg]')\n",
    "            ax.set_title('Walking Load vs Time')\n",
    "            ax.grid(True, alpha=0.3)\n",
    "            plt.tight_layout()\n",
    "            \n",
    "            # Store the figure\n",
    "            self.current_figure = fig\n",
    "            \n",
    "            plt.show()\n",
    "            \n",
    "            # Update button states\n",
    "            self.plot_button.disabled = True\n",
    "            self.save_plot_button.disabled = False\n",
    "    \n",
    "    def save_plot_handler(self, button):\n",
    "        \"\"\"Handle the save plot button click\"\"\"\n",
    "        if self.current_walking_load is None or self.current_figure is None:\n",
    "            with self.output_area:\n",
    "                print(\"Error: No plot to save. Please calculate and plot first!\")\n",
    "            return\n",
    "        \n",
    "        try:\n",
    "            # Get file name with inputs\n",
    "            step_frequency = self.step_frequency_input.value\n",
    "            body_mass = self.body_mass_input.value\n",
    "            nr_steps = self.number_of_steps_input.value\n",
    "            load_reduction = \"with_reduction\" if self.load_reduction_toggle.value else \"no_reduction\"\n",
    "            \n",
    "            filename = f\"walking_load_{step_frequency}Hz_{body_mass}kg_{nr_steps}steps_{load_reduction}.png\"\n",
    "            \n",
    "            # Save to user's Downloads folder\n",
    "            downloads_folder = Path.home() / \"Downloads\"\n",
    "            full_path = downloads_folder / filename\n",
    "            \n",
    "            # Save the existing figure\n",
    "            self.current_figure.savefig(full_path, dpi=300, bbox_inches='tight')\n",
    "            \n",
    "            with self.output_area:\n",
    "                print(\"Plot saved successfully!\")\n",
    "                print(f\"Filename: {filename}\")\n",
    "                print(f\"Location: {downloads_folder}\")\n",
    "                print(f\"Full path: {full_path}\")\n",
    "                \n",
    "        except Exception as e:\n",
    "            with self.output_area:\n",
    "                print(f\"Error saving plot: {str(e)}\")\n",
    "    \n",
    "    def clear_handler(self, button):\n",
    "        \"\"\"Handle the clear button click\"\"\"\n",
    "        # Clear outputs\n",
    "        with self.output_area:\n",
    "            self.output_area.clear_output()\n",
    "        with self.plot_output_area:\n",
    "            self.plot_output_area.clear_output()\n",
    "        \n",
    "        # Reset walking load and figure to None\n",
    "        self.current_walking_load = None\n",
    "        self.current_figure = None\n",
    "        \n",
    "        # Reset button states\n",
    "        self.calculate_button.disabled = False\n",
    "        self.plot_button.disabled = True\n",
    "        self.save_plot_button.disabled = True\n",
    "        \n",
    "        with self.output_area:\n",
    "            print(\"Walking load calculation is cleared and reset!\")\n",
    "\n",
    "\n",
    "# Create calculator instance\n",
    "calculator = WalkingLoadCalculator()\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
