{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "The Local Preprocessor that is in the process of being moved to the cloud. This is a \\*work in progress\\* and is not yet ready for use."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import time\n",
    "\n",
    "import trimesh\n",
    "import zipfile\n",
    "\n",
    "import numpy as np\n",
    "import math\n",
    "import json\n",
    "import xml\n",
    "import copy\n",
    "from scipy.spatial.transform import Rotation as R\n",
    "\n",
    "\n",
    "def get_rotation_matrix(vec2, vec1=np.array([1, 0, 0])):\n",
    "    \"\"\"get rotation matrix between two vectors using scipy\"\"\"\n",
    "    vec1 = np.reshape(vec1, (1, -1))\n",
    "    vec2 = np.reshape(vec2, (1, -1))\n",
    "    r = R.align_vectors(vec2, vec1)\n",
    "    return r[0].as_matrix()\n",
    "\n",
    "def findInZip(z, filepart):\n",
    "    return next(x for x in z.filelist if filepart in x.filename)\n",
    "\n",
    "def file(path):\n",
    "    with zipfile.ZipFile(path) as z:\n",
    "        landmarks = next(x for x in z.filelist if \"faceLandmarks\" in x.filename)\n",
    "        stlzip = next(x for x in z.filelist if \"headmesh\" in x.filename)\n",
    "\n",
    "        print(landmarks)\n",
    "        print()\n",
    "\n",
    "def directionalHit(intersector, samples, direction):\n",
    "    #hit = intersector.intersects_location(np.asarray([landmarks[0, i]]), direction)\n",
    "    #if len(hit[0]) < 1:\n",
    "    #    hit = intersector.intersects_location(np.asarray([landmarks[0, i]]), np.asarray([[0, 0, -1]]))\n",
    "    pass\n",
    "\n",
    "def alignToMeanFace(mesh):\n",
    "    pass\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Visualize the scan\n",
    "- [ ] testing lowering/raising the headset rather then asymitry\n",
    "- [ ] add collision checking\n",
    "- [ ] rendering of images\n",
    "- [ ] document and consolidate the validator\n",
    "- [ ] optimize performance \n",
    "- [ ] change to wrapping the tool as it is called in cloud repo"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "jobUrl=\"https://test-pie-arda.bigscreencloud.com/fabricator/job/P9yaWDVnVXc3JL0iGqZSnnwu3oI\"\n",
    "jobId = jobUrl.split(\"/\")[-1]\n",
    "\n",
    "# The auth server is used for authentication and account management\n",
    "authApiUrl = \"https://test-pie-api.bigscreencloud.com\"\n",
    "\n",
    "# The admin server has the fabricator endpoints\n",
    "adminApiUrl = \"https://test-pie-admin-api.bigscreencloud.com\"\n",
    "\n",
    "# Each http request needs a bearer token. This is a revokable token which is assigned to each external application that\n",
    "# uses the Bigscreen APIs (e.g. this script is an external application).\n",
    "apiBearerToken = \"mMrZSyycoLHGcYwIiOzCEwrgljQXwuEzG0lFJkq1GBsiNQCb0kTuLG8weL89sSbr\"\n",
    "\n",
    "# Finally, we need an account.  The account is necessary for us to identify who is using the various APIs. The account\n",
    "# must have access to the \"Fabricator\" endpoints to work.\n",
    "email = \"qa+fabricator@bigscreenvr.com\"\n",
    "password = \"EQDnMGe2tod26B2JgeWBi3W86oPkph9F\"\n",
    "\n",
    "\n",
    "# You may need to run this on Jupyter\n",
    "# !pip install requests\n",
    "\n",
    "import requests\n",
    "\n",
    "headers = {\n",
    "    \"Content-Type\": \"application/json\",\n",
    "    \"Authorization\": f\"Bearer {apiBearerToken}\"\n",
    "}\n",
    "\n",
    "payload = {\n",
    "    \"email\": email,\n",
    "    \"password\": password\n",
    "}\n",
    "\n",
    "r = requests.post(f\"{authApiUrl}/login\", headers=headers, json=payload)\n",
    "print(r.status_code)\n",
    "refreshToken = r.headers[\"x-refresh-token\"]\n",
    "accessToken = r.headers[\"x-access-token\"]\n",
    "\n",
    "def getAdminHeaders():\n",
    "    return {\n",
    "        \"Content-Type\": \"application/json\",\n",
    "        \"Authorization\": f\"Bearer {apiBearerToken}\",\n",
    "        \"x-access-token\": accessToken\n",
    "    }\n",
    "\n",
    "from enum import Enum\n",
    "\n",
    "r = requests.get(f\"{adminApiUrl}/admin/fabricator/schemas\", headers=getAdminHeaders())\n",
    "assert r.status_code == 200, f\"status code should be 200 (actual: {r.status_code})\"\n",
    "\n",
    "schemas = r.json()\n",
    "JobStates = Enum(\"JobStates\", dict([(v, int(k)) for k, v in schemas[\"JobStates\"].items()]))\n",
    "JobActions = Enum(\"JobActions\", dict([(v, int(k)) for k, v in schemas[\"JobActions\"].items()]))\n",
    "r = requests.get(f\"{adminApiUrl}/admin/fabricator/job/{jobId}/topology\", headers=getAdminHeaders())\n",
    "#assert r.status_code == 200, f\"status code should be 200 (actual: {r.status_code})\"\n",
    "\n",
    "r = requests.get(f\"{adminApiUrl}/admin/fabricator/job/{jobId}/scan\", headers=getAdminHeaders())\n",
    "assert r.status_code == 200, f\"status code should be 200 (actual: {r.status_code})\"\n",
    "\n",
    "# stream the requests output to a file\n",
    "with open(\"./tmp_scan.zip\", \"wb\") as f:\n",
    "    r.raw.decode_content = True\n",
    "    f.write(r.content)\n",
    "\n",
    "input_path = \"./tmp_scan.zip\"\n",
    "\n",
    "zipf = zipfile.ZipFile(input_path, 'r')\n",
    "lm = zipf.extract(findInZip(zipf,\"registeredLandmarks.pp\"))\n",
    "tree = xml.etree.ElementTree.parse(lm)\n",
    "#r = [x for x in tree.getroot().iter('point') if \"RPupil\" in x.attrib]\n",
    "lm = tree.findall(\"point\")\n",
    "rp = next(x.attrib for x in lm if 'RPupil' in x.attrib.values())\n",
    "lp = next(x.attrib for x in lm if 'LPupil' in x.attrib.values())\n",
    "right_pupil_from_pp = np.asarray([rp['x'], rp['y'], rp['z']], dtype=float)\n",
    "left_pupil_from_pp = np.asarray([lp['x'], lp['y'], lp['z']], dtype=float)\n",
    "\n",
    "lm = zipf.extract(findInZip(zipf,\"sharedScanMetadata.json\"))\n",
    "sharedScanMetadata = json.load(open(lm))\n",
    "right_pupil = np.asarray(sharedScanMetadata['farRightPupil'][:3])\n",
    "left_pupil = np.asarray(sharedScanMetadata['farLeftPupil'][:3])\n",
    "\n",
    "binocular_far_pd = np.linalg.norm(left_pupil - right_pupil)\n",
    "\n",
    "index = 0\n",
    "\n",
    "obj = zipf.extract(findInZip(zipf,\"headMesh.obj\"))\n",
    "zipf.extract(findInZip(zipf,\"headMesh.mtl\"))\n",
    "zipf.extract(findInZip(zipf,\"headMesh.jpg\"))\n",
    "zipf.extract(findInZip(zipf,\"movie.mov\"))\n",
    "\n",
    "mesh = trimesh.load(obj)\n",
    "mesh.show()\n",
    "index = index+1\n",
    "\n",
    "#meanFacePath = \"/Users/philipkrejov/PycharmProjects/testOpen3d/meanFace.obj\"\n",
    "#meanFace = trimesh.load(meanFacePath)\n",
    "\n",
    "cushPath = \"./CushionSubframe.stl\"\n",
    "##cushPath = \"/Users/philipkrejov/PycharmProjects/testOpen3d/BS Cushion Slim V07 FLAT.stl\"\n",
    "cushion = trimesh.load(cushPath)\n",
    "scene = trimesh.Scene()\n",
    "slices = trimesh.Scene()\n",
    "scene.camera_transform = trimesh.transformations.translation_matrix((0,0,300))\n",
    "\n",
    "# ray intersection with front of mesh\n",
    "intersector = trimesh.ray.ray_triangle.RayMeshIntersector(mesh)\n",
    "for i in [left_pupil, right_pupil]:\n",
    "    hit = intersector.intersects_location([i], np.asarray([[0,0,1]]))\n",
    "    if len(hit[0]) < 1:\n",
    "        hit = intersector.intersects_location([i], np.asarray([[0, 0, -1]]))\n",
    "    i = hit[0][0]\n",
    "\n",
    "offset = trimesh.transformations.translation_matrix(right_pupil)\n",
    "rot = get_rotation_matrix(left_pupil - right_pupil)\n",
    "T = np.identity(4)\n",
    "T[:3, :3] = rot\n",
    "mesh.apply_transform(np.linalg.inv(offset))\n",
    "mesh.apply_transform(np.linalg.inv(T))\n",
    "\n",
    "#meanFace.apply_transform(trimesh.transformations.translation_matrix((0,0,40)))\n",
    "#meanFace.apply_transform(np.linalg.inv(offset))\n",
    "#meanFace.apply_transform(np.linalg.inv(T))\n",
    "\n",
    "#samples = trimesh.sample.sample_surface_even(meanFace, 100)[0]\n",
    "#hit = rotatedIntersector.intersects_location([i], np.array([[0, 0, -1]]))\n",
    "\n",
    "#meanFace.visual.face_colors = (186,85,211, 100)\n",
    "\n",
    "eyeRelief = 14.5\n",
    "asymmetry = 2\n",
    "downward_tilt = 6\n",
    "\n",
    "binocular_far_pd = np.linalg.norm(left_pupil - right_pupil)\n",
    "before = trimesh.creation.axis(axis_radius=.1, axis_length=binocular_far_pd)\n",
    "before.apply_transform(T)\n",
    "scene.add_geometry(before)\n",
    "scene.add_geometry(mesh)\n",
    "#scene.add_geometry(meanFace)\n",
    "cushion.apply_transform(trimesh.transformations.rotation_matrix(-math.pi/2, (0,1,0)))\n",
    "cushion.apply_transform(trimesh.transformations.rotation_matrix(-math.pi/2, (0,0,1)))\n",
    "cushion.apply_transform(trimesh.transformations.translation_matrix(((binocular_far_pd-64)/2,0,0)))\n",
    "mesh.apply_transform(trimesh.transformations.translation_matrix([asymmetry,0,12-eyeRelief]))\n",
    "mesh.apply_transform(trimesh.transformations.rotation_matrix(-np.radians(downward_tilt), (1,0,0)))\n",
    "samples = trimesh.sample.sample_surface_even(cushion, 1000)[0]\n",
    "\n",
    "for i in samples:\n",
    "    s = trimesh.primitives.Sphere(radius=.5, center=i)\n",
    "    s.visual.face_colors = (255,0,0,255)\n",
    "    scene.add_geometry(s)\n",
    "\n",
    "scene.add_geometry(cushion)\n",
    "scene.add_geometry(trimesh.creation.axis(axis_radius=.1, axis_length=binocular_far_pd))\n",
    "for i in [left_pupil, right_pupil]:\n",
    "    l = trimesh.primitives.Sphere(radius=1, center=i)\n",
    "    l.visual.face_colors = (255,0,255,255)\n",
    "    l.apply_transform(np.linalg.inv(offset))\n",
    "    l.apply_transform(np.linalg.inv(T))\n",
    "    scene.add_geometry(l)\n",
    "\n",
    "trimesh.transformations.translation_matrix((i))\n",
    "print(binocular_far_pd)\n",
    "scene.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Export, Build and Submit Toolpath\n",
    "\n",
    "- [x] Transform for fusion\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "output_path = \"/Users/philipkrejov/toolpath/\"\n",
    "l = os.listdir(output_path)\n",
    "for p in l:\n",
    "    if p.endswith(\".stl\"):\n",
    "        os.remove(os.path.join(output_path, p))\n",
    "mesh_copy = mesh.copy()\n",
    "mesh_copy.apply_transform(trimesh.transformations.rotation_matrix(-math.pi/2, (1,0,0)))\n",
    "mesh_copy.apply_scale(.1)\n",
    "mesh_copy.export(f\"{output_path}test{round(ipd)}.stl\")\n",
    "scene2 = trimesh.Scene()\n",
    "mesh2 = trimesh.load(f'{output_path}test{round(ipd)}.stl')\n",
    "scene2.add_geometry(mesh2)\n",
    "origin = trimesh.creation.axis(axis_radius=.1, axis_length=ipd/10)\n",
    "scene2.add_geometry(origin)\n",
    "scene2.show()\n",
    "\n",
    "#set fusion env\n",
    "os.environ[\"PROCESS_DIR_PATH\"] = \"/Users/philipkrejov/PycharmProjects/testOpen3d/fusion\"\n",
    "os.environ['FUSIONS_PRODUCTION_FOLDER'] = '/Applications/Autodesk Fusion 360.app/Contents/Frameworks/Fusion360Core.framework/Resources/Production'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "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.9.10"
  },
  "vscode": {
   "interpreter": {
    "hash": "b0fa6594d8f4cbf19f97940f81e996739fb7646882a419484c72d19e05852a7e"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
