{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "nbsphinx": "hidden"
   },
   "outputs": [],
   "source": [
    "import open3d as o3d\n",
    "import numpy as np\n",
    "import os\n",
    "import sys\n",
    "\n",
    "# monkey patches visualization and provides helpers to load geometries\n",
    "sys.path.append('..')\n",
    "import open3d_tutorial as o3dtut\n",
    "# change to True if you want to interact with the visualization windows\n",
    "o3dtut.interactive = not \"CI\" in os.environ"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Mesh deformation\n",
    "If we want to deform a triangle mesh according to a small number of constraints, we can use mesh deformation algorithms. Open3D implements the as-rigid-as-possible method by [\\[SorkineAndAlexa2007\\]](../reference.html#SorkineAndAlexa2007) that optimizes the following energy function\n",
    "\n",
    "\\begin{equation}\n",
    "\\sum_i \\sum_{j \\in \\mathcal{N}(i)} w_{ij} || (\\mathbf{p}'_i - \\mathbf{p}'_j) - \\mathbf{R}_i (\\mathbf{p}_i - \\mathbf{p}_j)||^2 \\,,\n",
    "\\end{equation}\n",
    "\n",
    "where $\\mathbf{R}_i$ are the rotation matrices that we want to optimize for, and $\\mathbf{p}_i$ and $\\mathbf{p}'_i$ are the vertex positions before and after the optimization, respectively. $\\mathcal{N}(i)$ is the set of neighbors of vertex $i$. The weights $w_{ij}$ are cotangent weights.\n",
    "\n",
    "Open3D implements this method in `deform_as_rigid_as_possible`. The first argument to this method is a set of `constraint_ids` that refer to the vertices in the triangle mesh. The second argument `constrint_pos` defines at which position those vertices should be after the optimization. The optimization process is an iterative scheme. Hence, we also can define the number of iterations via `max_iter`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "armadillo = o3d.data.ArmadilloMesh()\n",
    "mesh = o3d.io.read_triangle_mesh(armadillo.path)\n",
    "\n",
    "vertices = np.asarray(mesh.vertices)\n",
    "static_ids = [idx for idx in np.where(vertices[:, 1] < -30)[0]]\n",
    "static_pos = []\n",
    "for id in static_ids:\n",
    "    static_pos.append(vertices[id])\n",
    "handle_ids = [2490]\n",
    "handle_pos = [vertices[2490] + np.array((-40, -40, -40))]\n",
    "constraint_ids = o3d.utility.IntVector(static_ids + handle_ids)\n",
    "constraint_pos = o3d.utility.Vector3dVector(static_pos + handle_pos)\n",
    "\n",
    "with o3d.utility.VerbosityContextManager(\n",
    "        o3d.utility.VerbosityLevel.Debug) as cm:\n",
    "    mesh_prime = mesh.deform_as_rigid_as_possible(constraint_ids,\n",
    "                                                  constraint_pos,\n",
    "                                                  max_iter=50)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('Original Mesh')\n",
    "R = mesh.get_rotation_matrix_from_xyz((0, np.pi, 0))\n",
    "o3d.visualization.draw_geometries([mesh.rotate(R, center=mesh.get_center())])\n",
    "print('Deformed Mesh')\n",
    "mesh_prime.compute_vertex_normals()\n",
    "o3d.visualization.draw_geometries(\n",
    "    [mesh_prime.rotate(R, center=mesh_prime.get_center())])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Smoothed ARAP\n",
    "Open3D also implements a smoothed version of the ARAP objective defined as \n",
    "\n",
    "\\begin{equation}\n",
    "\\sum_i \\sum_{j \\in \\mathcal{N}(i)} w_{ij} || (\\mathbf{p}'_i - \\mathbf{p}'_j) - \\mathbf{R}_i (\\mathbf{p}_i - \\mathbf{p}_j)||^2 + \\alpha A ||\\mathbf{R}_i - \\mathbf{R}_j||^2\\,,\n",
    "\\end{equation}\n",
    "\n",
    "that penalizes a deviation of neighboring rotation matrices. $\\alpha$ is a trade-off parameter for the regularization term and $A$ is the surface area. \n",
    "\n",
    "The smoothed objective can be used in `deform_as_rigid_as_possible` by using the argument `energy` with the parameter `Smoothed`."
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Edit Metadata",
  "kernelspec": {
   "display_name": "Python 3",
   "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.7.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
