{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "7e9e9a56-c560-4e9d-b912-b7f04cc42465",
   "metadata": {},
   "source": [
    "# Using SVG Visualizations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "39d7c8bf-c258-4de2-8f13-f5363cdc4d70",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'<circle cx=\"30.00\" cy=\"20.00\" r=\"5.00\" fill=\"red\" stroke=\"yellow\" />'"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Here is how the basic drawing works: functions that return text!\n",
    "import svg_snip.Elements as e2d\n",
    "e2d.circle(cx=30, cy=20, r=5, stroke='yellow', fill='red')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "b60a1e54-8486-46ec-8130-1113732803d4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<line x1=\"15\" y1=\"25\" x2=\"25\" y2=\"35\" stroke=\"white\"/>\n",
      "<line x1=\"15\" y1=\"35\" x2=\"25\" y2=\"25\" stroke=\"white\"/>\n"
     ]
    }
   ],
   "source": [
    "# you can actually write your own like so:\n",
    "def x(x=0, y=0, **kwargs):\n",
    "    return f\"\"\"\\\n",
    "<line x1=\"{x-5}\" y1=\"{y-5}\" x2=\"{x+5}\" y2=\"{y+5}\" stroke=\"white\"/>\n",
    "<line x1=\"{x-5}\" y1=\"{y+5}\" x2=\"{x+5}\" y2=\"{y-5}\" stroke=\"white\"/>\"\"\"\n",
    "\n",
    "print(x(20,30))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "ea8aa128-6d15-4bc4-8900-e2db856fbc55",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<details close><summary>show html</summary><pre>&lt;svg  width=&quot;100&quot; height=&quot;100&quot; viewBox=&quot;0 0 100 100&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;\n",
       "  &lt;rect x=&quot;0.00&quot; y=&quot;0.00&quot; width=&quot;100.00&quot; height=&quot;100.00&quot; /&gt;\n",
       "  &lt;line x1=&quot;45&quot; y1=&quot;45&quot; x2=&quot;55&quot; y2=&quot;55&quot; stroke=&quot;white&quot;/&gt;\n",
       "  &lt;line x1=&quot;45&quot; y1=&quot;55&quot; x2=&quot;55&quot; y2=&quot;45&quot; stroke=&quot;white&quot;/&gt;\n",
       "  &lt;g transform=&quot;translate(30, 20)&quot;&gt;\n",
       "    &lt;!-- This is the svg_g Group --&gt;\n",
       "    &lt;circle r=&quot;5.00&quot; fill=&quot;red&quot; stroke=&quot;yellow&quot; /&gt;\n",
       "  &lt;/g&gt;\n",
       "  &lt;g transform=&quot;translate(45, 25)&quot;&gt;\n",
       "    &lt;!-- This is the svg_g Group --&gt;\n",
       "    &lt;circle r=&quot;5.00&quot; fill=&quot;red&quot; stroke=&quot;yellow&quot; /&gt;\n",
       "  &lt;/g&gt;\n",
       "&lt;/svg&gt;</pre></details>\n",
       "<svg  width=\"100\" height=\"100\" viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">\n",
       "  <rect x=\"0.00\" y=\"0.00\" width=\"100.00\" height=\"100.00\" />\n",
       "  <line x1=\"45\" y1=\"45\" x2=\"55\" y2=\"55\" stroke=\"white\"/>\n",
       "  <line x1=\"45\" y1=\"55\" x2=\"55\" y2=\"45\" stroke=\"white\"/>\n",
       "  <g transform=\"translate(30, 20)\">\n",
       "    <!-- This is the svg_g Group -->\n",
       "    <circle r=\"5.00\" fill=\"red\" stroke=\"yellow\" />\n",
       "  </g>\n",
       "  <g transform=\"translate(45, 25)\">\n",
       "    <!-- This is the svg_g Group -->\n",
       "    <circle r=\"5.00\" fill=\"red\" stroke=\"yellow\" />\n",
       "  </g>\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from svg_snip.Composer import Composer\n",
    "from svg_snip.Composer import Group, comment\n",
    "\n",
    "svg = Composer((100,100))\n",
    "svg.add(e2d.rect, x=0, y=0, width=100, height=100)\n",
    "svg.add(x, x=50, y=50)\n",
    "\n",
    "svg_g = Group()\n",
    "svg_g.add(comment, text=\"This is the svg_g Group\")\n",
    "svg_g.add(e2d.circle, r=5, stroke='yellow', fill='red')\n",
    "\n",
    "# draw the group in multiple locations\n",
    "svg.add(svg_g, transform='translate(30, 20)')\n",
    "svg.add(svg_g, transform='translate(45, 25)')\n",
    "\n",
    "\n",
    "svg.display(debug=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "141dfd77-45af-4137-8467-0a6bee0a78e6",
   "metadata": {},
   "source": [
    "# How to interact with simple UI elements\n",
    "\n",
    "Jupyter is decently fast with updating short HTML fragments.\n",
    "You loose most time because running the python kernel in the backend and sending resulting text to the frontend.\n",
    "\n",
    "For debugging and scientific visualizations, this is quite fast enough."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "4aa0a3df-a00d-4b0e-998e-efafcf3e39d6",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "6d8079c3bfc947098198234ef25301a2",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "IntSlider(value=0, description='Angle', max=360, step=2)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "c1f211ab393143299ce97ff32c7a7ccd",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Output()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import ipywidgets as w\n",
    "from IPython.display import clear_output, display\n",
    "\n",
    "s = w.IntSlider(value=0, min=0, max=360, step=2, description='Angle')\n",
    "out = w.Output()\n",
    "\n",
    "def update(c):\n",
    "    with out:\n",
    "        clear_output(wait=True)\n",
    "        svg = Composer((100,100))\n",
    "        svg.add(e2d.rect, x=0, y=0, width=100, height=100)\n",
    "        g = Group()\n",
    "        g.add(e2d.rect, x=20, y=20, width=30, height=30, fill='blue')\n",
    "        svg.add(g, transform=f'rotate({c[\"new\"]} 50 50)')\n",
    "        svg.display()\n",
    "\n",
    "s.observe(update, names='value')\n",
    "display(s, out)\n",
    "update({\"new\": s.value})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ff734d53-40a9-4a98-a002-918592912223",
   "metadata": {},
   "source": [
    "# Using 3D Visualizations\n",
    "\n",
    "This example shows how CanvasWithOverlay also supports basic mouse events.\n",
    "\n",
    "This is easily fast enough to support interactively tuning parameters and rotating the camera for simple 3D geometry.\n",
    "\n",
    "SVG itself does not suport 3D rendering. The python backend does the heavy lifting.\n",
    "\n",
    "The results is a vector graphic that is easily exported and used in papers, for example.\n",
    "\n",
    "Note that the backface removal is based on the normal. So shaded faces work only for simple, convex geometry."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "6b749423-44b2-4842-9638-ec351c97ff9d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "a0ae858839fb42a58b2504c7ce5aad55",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "IntSlider(value=0, description='R', max=255)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "5e51e61fa0e6454eb083b32aa0468808",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "IntSlider(value=120, description='G', max=255)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "3c0e5eafe2ca478499e27cea3bb93351",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "IntSlider(value=255, description='B', max=255)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "85c1ca36a6b240c9b1df43c7afa6caae",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(HTML(value='\\n<style>\\n  .dimension {\\n    top: 0;\\n    left: 0;\\n    width: 500px;\\n    height…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from svg_snip.Jupyter import CanvasWithOverlay\n",
    "import svg_snip.Elements as e2d\n",
    "import svg_snip.Elements3D as e3d\n",
    "import ipywidgets as w\n",
    "\n",
    "import numpy as np\n",
    "\n",
    "from ProjectiveGeometry23.central_projection import ProjectionMatrix\n",
    "from ProjectiveGeometry23.homography import rotation_x, rotation_z\n",
    "\n",
    "P_lookat = ProjectionMatrix.perspective_look_at(\n",
    "    eye=np.array([0, 0, 250]),\n",
    "    center=np.array([0, 0, 0]),\n",
    "    image_size=(500, 500),\n",
    "    fovy_rad=0.7\n",
    ")\n",
    "\n",
    "# Color sliders\n",
    "r = w.IntSlider(value=0, min=0, max=255, description='R')\n",
    "g = w.IntSlider(value=120, min=0, max=255, description='G')\n",
    "b = w.IntSlider(value=255, min=0, max=255, description='B')\n",
    "\n",
    "vis = CanvasWithOverlay(500, 500)\n",
    "\n",
    "ax = az = 1\n",
    "\n",
    "def draw():\n",
    "    svg = Composer((vis.w, vis.h))\n",
    "\n",
    "    svg.add(\n",
    "        e3d.cube,\n",
    "        min=[-50,-50,-50],\n",
    "        max=[50,50,50],\n",
    "        fill=f'rgb({r.value},{g.value},{b.value})',\n",
    "        stroke='black'\n",
    "    )\n",
    "\n",
    "    svg.add(e2d.star, x=vis.mouse_state.x, y=vis.mouse_state.y, size=8,\n",
    "        fill=\"red\" if vis.mouse_state.clicked else \"black\")\n",
    "    \n",
    "    T = rotation_x(ax) @ rotation_z(az)\n",
    "    vis.html_overlay.value = svg.render(P=P_lookat.P @ T)\n",
    "\n",
    "\n",
    "def handle_draw(vis):\n",
    "    global ax\n",
    "    global az\n",
    "\n",
    "    if vis.mouse_state.clicked:\n",
    "        az += vis.mouse_state.dx * -0.01\n",
    "        ax += vis.mouse_state.dy * 0.01\n",
    "\n",
    "    draw()\n",
    "\n",
    "vis.handle_draw = handle_draw\n",
    "\n",
    "# Redraw when sliders change\n",
    "for s in (r, g, b):\n",
    "    s.observe(lambda _: draw(), names='value')\n",
    "\n",
    "display(r, g, b)\n",
    "vis.display()\n",
    "draw()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "88ac882f-0ce1-4d19-b195-0a7e68062812",
   "metadata": {},
   "outputs": [],
   "source": [
    "with open('output/example_3D_cube.svg', 'w') as file:\n",
    "    file.write(vis.html_overlay.value)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4ba61a8d-49c7-435c-b2a9-8412e64f8ea1",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "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.14.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
