{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a860ebe8",
   "metadata": {},
   "source": [
    "# Basic frames usage\n",
    "Illustrates frames layer usage"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0fe84058",
   "metadata": {},
   "source": [
    "This notebook demonstrates how to use the _Frames_ layer in **kdFlex** for working with reference frames.\n",
    "\n",
    "<img src=../resources/nb_images/frames.png align=center width=300>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "e5d32d70",
   "metadata": {},
   "outputs": [],
   "source": [
    "import Karana.Math as km\n",
    "import Karana.Frame as kf"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "51bfb946",
   "metadata": {},
   "source": [
    "# Frame container\n",
    "\n",
    "The reference frames we create will all be managed by a `FrameContainer`. Here, the `\"root_fr\"` argument is the name of an implicitly created root `Frame`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "0ca4e3a3",
   "metadata": {},
   "outputs": [],
   "source": [
    "frames = kf.FrameContainer(\"root_fr\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5919f554",
   "metadata": {},
   "source": [
    "Defining a helper function to visualize the relationship between reference frames. We'll use this throughout the notebook to see what is going on:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f4cb0ca4",
   "metadata": {},
   "outputs": [],
   "source": [
    "def visualizeFrames(frames: kf.FrameContainer):\n",
    "    \"\"\"Visualize a set of frames.\n",
    "\n",
    "    Parameters\n",
    "    ----------\n",
    "    frames : kf.FrameContainer\n",
    "        Container with all frames to show.\n",
    "    \"\"\"\n",
    "    from IPython.display import display, IFrame\n",
    "\n",
    "    display_server = frames.showFramesGraph(port=0, chains=True, print_connection_info=False)\n",
    "    display(IFrame(src=display_server.getUrl(), width=\"100%\", height=\"400px\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e7e7bd4",
   "metadata": {},
   "source": [
    "As you can see, a `Frame` with the name, `\"root_fr\"` has automatically been created. This frame forms the root of the `Frame` tree we will build up."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "c88ffb77",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"100%\"\n",
       "            height=\"400px\"\n",
       "            src=\"http://newton:49445\"\n",
       "            frameborder=\"0\"\n",
       "            allowfullscreen\n",
       "            \n",
       "        ></iframe>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.lib.display.IFrame at 0x7ace8e549b20>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "visualizeFrames(frames)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "80643fe7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'root_fr'"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "root_fr = frames.root()\n",
    "root_fr.name()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95a9ba69",
   "metadata": {},
   "source": [
    "# Adding a reference frame\n",
    "\n",
    "Here we create another `Frame`, `ch1_fr`, whose properties will be defined relative to `root_fr`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "bd4bb847",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"100%\"\n",
       "            height=\"400px\"\n",
       "            src=\"http://newton:50285\"\n",
       "            frameborder=\"0\"\n",
       "            allowfullscreen\n",
       "            \n",
       "        ></iframe>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.lib.display.IFrame at 0x7ace8e569460>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "ch1_fr = kf.Frame(\"ch1_fr\", frames)\n",
    "visualizeFrames(frames)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9dba42e4",
   "metadata": {},
   "source": [
    "At this point it is unspecified how `ch1_fr` relates to the rest of the frame tree. To do anything useful, we must first attach `ch1_fr` to another `Frame`. This can be done by creating a `PrescribedFrameToFrame`, to define the pose of `ch1_fr` relative to its parent frame in the tree:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "0a28263d",
   "metadata": {},
   "outputs": [],
   "source": [
    "root_to_ch1 = kf.PrescribedFrameToFrame(root_fr, ch1_fr)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30c16b73",
   "metadata": {},
   "source": [
    "Now we can see that there is an edge from `root_fr` to `ch1_fr`, that defines their relative transform, velocity, and acceleration:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "d8ddbdb1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"100%\"\n",
       "            height=\"400px\"\n",
       "            src=\"http://newton:44331\"\n",
       "            frameborder=\"0\"\n",
       "            allowfullscreen\n",
       "            \n",
       "        ></iframe>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.lib.display.IFrame at 0x7acdded1a990>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Karana.Math.HomTran(\n",
      "\tKarana.Math.UnitQuaternion(0, 0, 0, 1),\n",
      "\tarray([0., 0., 0.])\n",
      ")\n",
      "Karana.Math.SpatialVelocity(\n",
      "\t<Quantity([0. 0. 0.], '1 / second')>,\n",
      "\t<Quantity([0. 0. 0.], 'meter / second')>\n",
      ")\n",
      "Karana.Math.SpatialAcceleration(\n",
      "\t<Quantity([0. 0. 0.], '1 / second ** 2')>,\n",
      "\t<Quantity([0. 0. 0.], 'meter / second ** 2')>\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "visualizeFrames(frames)\n",
    "print(root_to_ch1.relTransform())\n",
    "print(root_to_ch1.relSpVel())\n",
    "print(root_to_ch1.relSpAccel())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24cae38e",
   "metadata": {},
   "source": [
    " In practice, a `FrameToFrame` will usually be updated by system dynamics, or even based on the ephemerides of celestial bodies via [SPICE](https://naif.jpl.nasa.gov/naif/index.html) data. Here, for the sake of example, we'll set the transform directly:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "e9935c16",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Karana.Math.HomTran(\n",
       "\tKarana.Math.UnitQuaternion(0, 0, 0, 1),\n",
       "\tarray([1., 2., 3.])\n",
       ")"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "root_to_ch1.setRelTransform(km.HomTran(vec=[1, 2, 3]))\n",
    "root_to_ch1.relTransform()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c67b7acf",
   "metadata": {},
   "source": [
    "Similarly, we can assign a relative spatial velocity:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "031ac061",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Karana.Math.SpatialVelocity(\n",
       "\t<Quantity([10.  0.  0.], '1 / second')>,\n",
       "\t<Quantity([0. 0. 0.], 'meter / second')>\n",
       ")"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "omega_x = km.SpatialVelocity([10, 0, 0], [0, 0, 0])\n",
    "root_to_ch1.setRelSpVel(omega_x)\n",
    "root_to_ch1.relSpVel()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a3e10229",
   "metadata": {},
   "source": [
    "As well as a relative spatial acceleration:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "746b44a1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Karana.Math.SpatialAcceleration(\n",
       "\t<Quantity([0.1 0.  0. ], '1 / second ** 2')>,\n",
       "\t<Quantity([0. 0. 0.], 'meter / second ** 2')>\n",
       ")"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "alpha_x = km.SpatialAcceleration([0.1, 0, 0], [0, 0, 0])\n",
    "root_to_ch1.setRelSpAccel(alpha_x)\n",
    "root_to_ch1.relSpAccel()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cfb3d45a",
   "metadata": {},
   "source": [
    "For any `FrameToFrame`, we refer to the first, typically more inboard frame as the _oframe_, and the second frame as the _pframe_:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "2253ad0e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "root_to_ch1.oframe() == root_fr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "c32d7540",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "root_to_ch1.pframe() == ch1_fr"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "65d225a1",
   "metadata": {},
   "source": [
    "## Chained frame to frames\n",
    "First we create another child frame in a similar manner:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "940ece61",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"100%\"\n",
       "            height=\"400px\"\n",
       "            src=\"http://newton:55023\"\n",
       "            frameborder=\"0\"\n",
       "            allowfullscreen\n",
       "            \n",
       "        ></iframe>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.lib.display.IFrame at 0x7acdded1be60>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "ch2_fr = kf.Frame(\"ch2_fr\", frames)\n",
    "root_to_ch2 = kf.PrescribedFrameToFrame(root_fr, ch2_fr)\n",
    "root_to_ch2.setRelTransform(km.HomTran(vec=[1, 2, 5]))\n",
    "visualizeFrames(frames)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cd8784ca",
   "metadata": {},
   "source": [
    "We often need to compute the relative spatial properties of an arbitrary pair of frames. Rather than manually walking and composing transforms along the tree of frames, we can get a so-called `ChainedFrameToFrame` to query relative properties between any pair of frames. In our visualization, this is represented as a dotted line. Note that they have a relative translation of $2$ in the $z$-direction:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "be5c371b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"100%\"\n",
       "            height=\"400px\"\n",
       "            src=\"http://newton:44047\"\n",
       "            frameborder=\"0\"\n",
       "            allowfullscreen\n",
       "            \n",
       "        ></iframe>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.lib.display.IFrame at 0x7acdded1be60>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Karana.Math.HomTran(\n",
      "\tKarana.Math.UnitQuaternion(0, 0, 0, 1),\n",
      "\tarray([0., 0., 2.])\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "ch1_to_ch2 = ch1_fr.frameToFrame(ch2_fr)\n",
    "visualizeFrames(frames)\n",
    "print(ch1_to_ch2.relTransform())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d7ca958d",
   "metadata": {},
   "source": [
    "As seen earlier, we can also query the relative spatial velocity and acceleration of `ch1` relative to `ch2`. Since `ch2` has an offset relative to `ch1`, and `ch1` is rotating, `ch2` has a linear velocity relative to `ch1`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "28901010",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Karana.Math.SpatialVelocity(\n",
      "\t<Quantity([-10.   0.   0.], '1 / second')>,\n",
      "\t<Quantity([ 0. 20.  0.], 'meter / second')>\n",
      ")\n",
      "Karana.Math.SpatialAcceleration(\n",
      "\t<Quantity([-0.1  0.   0. ], '1 / second ** 2')>,\n",
      "\t<Quantity([ 0.e+00  2.e-01 -2.e+02], 'meter / second ** 2')>\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "print(ch1_to_ch2.relSpVel())\n",
    "print(ch1_to_ch2.relSpAccel())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "7b323afa",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function __main__.cleanup()>"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import atexit\n",
    "\n",
    "\n",
    "def cleanup():\n",
    "    \"\"\"Clean up the simulation.\"\"\"\n",
    "    global root_fr, ch1_fr, ch2_fr, root_to_ch1, root_to_ch2, ch1_to_ch2\n",
    "    del root_fr, ch1_fr, ch2_fr, root_to_ch1, root_to_ch2, ch1_to_ch2\n",
    "\n",
    "    import Karana.Core as kc\n",
    "\n",
    "    kc.discard(frames)\n",
    "\n",
    "\n",
    "atexit.register(cleanup)"
   ]
  }
 ],
 "metadata": {
  "docs": {
   "tags": [
    "frames"
   ]
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
