diff --git a/.github/workflows/viewer_client_app.yml b/.github/workflows/viewer_client_app.yml index 49d096997..10b889676 100644 --- a/.github/workflows/viewer_client_app.yml +++ b/.github/workflows/viewer_client_app.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@master - name: Install Node.js - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: node-version: 17.8.0 @@ -54,4 +54,4 @@ jobs: uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./nerfactory/viewer/app/build + publish_dir: ./nerfactory/viewer/app/build \ No newline at end of file diff --git a/.gitignore b/.gitignore index 76b54487c..afd28f97a 100644 --- a/.gitignore +++ b/.gitignore @@ -121,6 +121,7 @@ celerybeat.pid # Environments .env +.envrc .venv env/ venv/ diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 000000000..3b519289b --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,6 @@ +module.exports = { + trailingComma: 'all', + arrowParens: 'always', + singleQuote: true, + jsxSingleQuote: false, + }; \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 962180731..953a075df 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,13 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "Launch Chrome against localhost", + "request": "launch", + "type": "chrome", + "url": "http://localhost:4000", + "webRoot": "${workspaceFolder}/nerfactory/viewer/app" + }, { "name": "Python: Current File", "type": "python", @@ -25,7 +32,9 @@ "request": "launch", "program": "scripts/run_train.py", "console": "integratedTerminal", - "args": ["machine.num_gpus=0"] + "args": [ + "machine.num_gpus=0" + ] }, { "name": "Python: run_train ngp (gpu)", @@ -33,7 +42,9 @@ "request": "launch", "program": "scripts/run_train.py", "console": "integratedTerminal", - "args": ["--config-name=graph_instant_ngp.yaml"] + "args": [ + "--config-name=graph_instant_ngp.yaml" + ] }, { "name": "Python: run_train mipnerf 360 (gpu)", @@ -41,7 +52,9 @@ "request": "launch", "program": "scripts/run_train.py", "console": "integratedTerminal", - "args": ["--config-name=graph_mipnerf_360.yaml"] + "args": [ + "--config-name=graph_mipnerf_360.yaml" + ] }, { "name": "Python: run_train nerfw (gpu)", @@ -66,4 +79,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 6a295ac34..aadaeb8e8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,6 +8,21 @@ "**/temp*": true }, "editor.defaultFormatter": "esbenp.prettier-vscode", + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "prettier.configPath": ".prettierrc.js", + "typescript.suggestionActions.enabled": false, + "javascript.suggestionActions.enabled": false, "[python]": { "editor.defaultFormatter": "ms-python.python", "editor.codeActionsOnSave": { @@ -17,11 +32,17 @@ "editor.formatOnSave": true, "python.envFile": "${workspaceFolder}/.env", "python.formatting.provider": "black", - "python.formatting.blackArgs": ["--line-length=120"], + "python.formatting.blackArgs": [ + "--line-length=120" + ], "python.linting.pylintEnabled": true, "python.linting.flake8Enabled": false, "python.linting.enabled": true, - "python.sortImports.args": ["--src=${workspaceFolder}", "--profile", "black"], + "python.sortImports.args": [ + "--src=${workspaceFolder}", + "--profile", + "black" + ], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, "python.linting.ignorePatterns": [ @@ -29,6 +50,16 @@ ".vscode/*.py", "external/**/*.py" ], + "esbonio.sphinx.confDir": "", + // eslint extension options + "javascript.validate.enable": false, + "eslint.enable": true, + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact" + ], "C_Cpp.errorSquiggles": "Disabled", "files.associations": { "array": "cpp", @@ -96,4 +127,4 @@ "__node_handle": "cpp" }, "python.analysis.typeCheckingMode": "basic" -} +} \ No newline at end of file diff --git a/docs/notebooks/imgs/nerfviewer.png b/docs/notebooks/imgs/nerfviewer.png new file mode 100644 index 000000000..dd7bcc941 Binary files /dev/null and b/docs/notebooks/imgs/nerfviewer.png differ diff --git a/docs/notebooks/index.rst b/docs/notebooks/index.rst index 6279c1d3d..c68884456 100644 --- a/docs/notebooks/index.rst +++ b/docs/notebooks/index.rst @@ -11,4 +11,5 @@ Components visualize_samplers.ipynb visualize_spatial_distortions.ipynb visualize_encoders.ipynb + visualize_viewer.ipynb \ No newline at end of file diff --git a/docs/notebooks/visualize_viewer.ipynb b/docs/notebooks/visualize_viewer.ipynb new file mode 100644 index 000000000..00313a672 --- /dev/null +++ b/docs/notebooks/visualize_viewer.ipynb @@ -0,0 +1,521 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "3c00934d-0f14-4c1a-a563-94d627b377e5", + "metadata": {}, + "source": [ + "# Visualize with our Viewer\n", + "\n", + "The real-time web viewer enables communication between Python and the ReactJS Client Viewer Application. Here we provide a walkthrough for how to use the viewer to draw information as well as read information from the GUI.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "id": "b15dfdc1-5054-40bc-9631-4c7426eecfd9", + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The autoreload extension is already loaded. To reload it, use:\n", + " %reload_ext autoreload\n" + ] + } + ], + "source": [ + "# HIDDEN\n", + "%load_ext autoreload\n", + "%autoreload 2\n", + "import pprint\n", + "import torch\n", + "import numpy as np\n", + "import torch\n", + "import mediapy as media\n", + "from nerfactory.data.structs import SceneBounds\n", + "from nerfactory.cameras.cameras import PinholeCamera\n", + "import time" + ] + }, + { + "cell_type": "markdown", + "id": "a8709c84", + "metadata": {}, + "source": [ + "## Open the Nerfviewer client\n", + "\n", + "Open the Nerfviewer client by visiting http://nerfviewer.plenoptix.ai/ or running the code locally. *We refer to this document for running the code locally.* You should see a GUI like the following:\n", + "\n", + "```{image} imgs/nerfviewer.png\n", + ":align: center\n", + ":class: only-light\n", + ":width: 600\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "052af8ce", + "metadata": {}, + "source": [ + "## Start the Bridge Server\n", + "\n", + "Run the following code in the repository home directory\n", + "\n", + "```\n", + "python scripts/run_viewer_bridge_server.py\n", + "```\n", + "\n", + "This will start the bridge server that both the Python code and Client Viewer App will connect to.\n", + "\n", + "Verify that the Client Viewer App is connected." + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "id": "189b2282", + "metadata": {}, + "outputs": [], + "source": [ + "# from IPython.display import HTML\n", + "\n", + "# url=\"http://localhost:4000/?localhost:18051\"\n", + "# height = 500\n", + "\n", + "# HTML(\"\"\"\n", + "#
\n", + "# \n", + "#
\n", + "# \"\"\".format(url=url, height=height))" + ] + }, + { + "cell_type": "markdown", + "id": "46c89306", + "metadata": {}, + "source": [ + "## Communicate with the Client Viewer App from Python\n", + "\n", + "Run the following code in the repository home directory\n", + "\n", + "```\n", + "python scripts/run_viewer_bridge_server.py\n", + "```\n", + "\n", + "This will start the bridge server that both the Python code and Client Viewer App will connect to.\n", + "\n", + "Verify that the Client Viewer App is connected." + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "id": "8919ad68", + "metadata": {}, + "outputs": [], + "source": [ + "# connect to the viewer from Python\n", + "from nerfactory.viewer.server.visualizer import Viewer\n", + "\n", + "vis = Viewer(zmq_url=\"tcp://127.0.0.1:6000\")" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "id": "605e0131-bfbe-4309-b338-c49a46923d8f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/sceneState/sceneBounds\n" + ] + } + ], + "source": [ + "# draw scene bounds in the viewer\n", + "s = 1\n", + "scene_bounds = SceneBounds(aabb=torch.tensor([[-s, -s, -s], [s, s, s]]))\n", + "scene_bounds_json = scene_bounds.to_json()\n", + "\n", + "# draw scene bounds in the viewer\n", + "vis[\"sceneState/sceneBounds\"].write(scene_bounds_json)\n", + "\n", + "# draw scene bounds in plotly\n", + "import plotly.graph_objects as go\n", + "\n", + "# TODO:" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "id": "fa0acb3c", + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [], + "source": [ + "# HIDDEN\n", + "def get_random_rotation_matrix():\n", + " roll = torch.randn(1)\n", + " yaw = torch.randn(1)\n", + " pitch = torch.randn(1)\n", + "\n", + " tensor_0 = torch.zeros(1)\n", + " tensor_1 = torch.ones(1)\n", + "\n", + " RX = torch.stack(\n", + " [\n", + " torch.stack([tensor_1, tensor_0, tensor_0]),\n", + " torch.stack([tensor_0, torch.cos(roll), -torch.sin(roll)]),\n", + " torch.stack([tensor_0, torch.sin(roll), torch.cos(roll)]),\n", + " ]\n", + " ).reshape(3, 3)\n", + "\n", + " RY = torch.stack(\n", + " [\n", + " torch.stack([torch.cos(pitch), tensor_0, torch.sin(pitch)]),\n", + " torch.stack([tensor_0, tensor_1, tensor_0]),\n", + " torch.stack([-torch.sin(pitch), tensor_0, torch.cos(pitch)]),\n", + " ]\n", + " ).reshape(3, 3)\n", + "\n", + " RZ = torch.stack(\n", + " [\n", + " torch.stack([torch.cos(yaw), -torch.sin(yaw), tensor_0]),\n", + " torch.stack([torch.sin(yaw), torch.cos(yaw), tensor_0]),\n", + " torch.stack([tensor_0, tensor_0, tensor_1]),\n", + " ]\n", + " ).reshape(3, 3)\n", + "\n", + " R = torch.mm(RZ, RY)\n", + " R = torch.mm(R, RX)\n", + " return R" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "id": "88b145d3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/sceneState/cameras\n" + ] + }, + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/sceneState/cameras/000000\n", + "/sceneState/cameras/000001\n" + ] + }, + { + "data": { + "text/plain": [ + "b'ok'" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# delete any current cameras\n", + "vis[f\"sceneState/cameras\"].delete()\n", + "\n", + "# draw a camera with with an image\n", + "image = torch.rand(256, 256, 3)\n", + "media.show_image(image)\n", + "camera = PinholeCamera(cx=100, cy=100, fx=50, fy=50)\n", + "camera_json = camera.to_json(image=image)\n", + "vis[f\"sceneState/cameras/000000\"].write(camera_json)\n", + "\n", + "# draw a camera with rotation and translation (with a black image)\n", + "image = torch.zeros(256, 256, 3)\n", + "c2w = torch.eye(4)\n", + "c2w[:3, :3] = get_random_rotation_matrix()\n", + "random_translation = torch.rand(1) * 5\n", + "c2w[:3, 3] = random_translation\n", + "camera = PinholeCamera(cx=100, cy=100, fx=50, fy=50, camera_to_world=c2w)\n", + "camera_json = camera.to_json(image=image)\n", + "vis[f\"sceneState/cameras/000001\"].write(camera_json)" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "id": "18991f81", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/sceneState/cameras/000000\n" + ] + }, + { + "data": { + "text/plain": [ + "b'ok'" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# delete a camera from the Nerfviewer\n", + "vis[\"sceneState/cameras/000000\"].delete()" + ] + }, + { + "cell_type": "markdown", + "id": "6b798e26", + "metadata": {}, + "source": [ + "## Read from the GUI sliders" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "id": "31cd0de1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "80\n" + ] + } + ], + "source": [ + "# read the field of view\n", + "camera = vis[f\"renderingState/camera\"].read()\n", + "fov = camera[\"object\"][\"fov\"]\n", + "print(fov)" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "id": "feb83aaa", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/renderingState/field_of_view\n" + ] + }, + { + "data": { + "text/plain": [ + "b'ok'" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "vis[f\"renderingState/field_of_view\"].write(40)" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "id": "1abb93e5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n" + ] + } + ], + "source": [ + "fov = vis[f\"renderingState/camera/object/fov\"].read()\n", + "print(fov)" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "id": "a0304523", + "metadata": {}, + "outputs": [], + "source": [ + "# set the field of view\n", + "camera = vis[f\"renderingState/camera\"].read()\n", + "fov = camera[\"object\"][\"fov\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "id": "b3ba0ee7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/renderingState/minResolution\n" + ] + }, + { + "data": { + "text/plain": [ + "b'ok'" + ] + }, + "execution_count": 83, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "vis[f\"renderingState/minResolution\"].write(20)" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "id": "bba59227", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "20" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# read from a slider\n", + "vis[f\"renderingState/minResolution\"].read()" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "id": "fe922185", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'depth'" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# read from output choice\n", + "vis[\"renderingState/output_choice\"].read()" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "id": "75c37760", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['rgb', 'accumulation', 'depth', 'alive_ray_mask']\n", + "None\n" + ] + } + ], + "source": [ + "# read from a dropdown\n", + "output_options = vis[f\"renderingState/output_options\"].read()\n", + "print(output_options)\n", + "output_choice = vis[f\"renderingState/output_choice\"].read()\n", + "print(output_choice)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "98aaa12c", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.8.13 ('nerfactory')", + "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.8.13" + }, + "vscode": { + "interpreter": { + "hash": "e691f033e0f2f1b9c0a11e7e81375a1e27aec87a71fd1b1eaa545f7a5e61b3f6" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/nerfactory/cameras/cameras.py b/nerfactory/cameras/cameras.py index 10e0b9a25..d46e3ba78 100644 --- a/nerfactory/cameras/cameras.py +++ b/nerfactory/cameras/cameras.py @@ -17,14 +17,16 @@ Camera Models """ import base64 from abc import abstractmethod -from typing import Dict, Optional, Type +from typing import Dict, Optional, Tuple, Type import cv2 +import numpy as np import torch from torch.nn.functional import normalize from torchtyping import TensorType from nerfactory.cameras.rays import RayBundle +from nerfactory.utils.misc import is_not_none class Camera: @@ -161,11 +163,15 @@ class Camera: return ray_bundle @abstractmethod - def to_json(self, image: Optional[TensorType["image_height", "image_width", 2]] = None) -> Dict: + def to_json( + self, + image: Optional[TensorType["image_height", "image_width", 2]] = None, + resize_shape: Optional[Tuple[int, int]] = None, + ) -> Dict: """Converts the camera to a json dictionary. Args: - image: An image that is encoded to a base64 string. Defaults to None. + image: An image in range [0, 1] that is encoded to a base64 string. Defaults to None. Returns: A JSON representation of the camera @@ -295,7 +301,11 @@ class PinholeCamera(Camera): return RayBundle(origins=origins, directions=directions, pixel_area=pixel_area[..., None]) - def to_json(self, image: Optional[TensorType["image_height", "image_width", 2]] = None) -> Dict: + def to_json( + self, + image: Optional[TensorType["image_height", "image_width", 2]] = None, + resize_shape: Optional[Tuple[int, int]] = None, + ) -> Dict: json_ = { "type": "PinholeCamera", "cx": self.cx, @@ -305,10 +315,11 @@ class PinholeCamera(Camera): "camera_to_world": self.camera_to_world.tolist(), "camera_index": self.camera_index, } - if image: - # move image to cpu if not already - # TODO: move to numpy - data = cv2.imencode(".png", image[:, :, ::-1])[1].tobytes() + if is_not_none(image): + image_uint8 = (image * 255).detach().cpu().numpy().astype(np.uint8) + if resize_shape: + image_uint8 = cv2.resize(image_uint8, resize_shape) + data = cv2.imencode(".png", image_uint8)[1].tobytes() json_["image"] = str("data:image/png;base64," + base64.b64encode(data).decode("ascii")) return json_ @@ -346,9 +357,6 @@ class SimplePinholeCamera(PinholeCamera): def fy_index(cls): return 2 - def to_json(self, image: Optional[TensorType["image_height", "image_width", 2]] = None) -> Dict: - raise NotImplementedError - @staticmethod def from_json(json_: Dict) -> "SimplePinholeCamera": # pylint:disable=no-self-use raise NotImplementedError @@ -441,7 +449,11 @@ class EquirectangularCamera(Camera): pixel_area = dx * dy return RayBundle(origins=origins, directions=directions, pixel_area=pixel_area[..., None]) - def to_json(self, image: Optional[TensorType["image_height", "image_width", 2]] = None) -> Dict: + def to_json( + self, + image: Optional[TensorType["image_height", "image_width", 2]] = None, + resize_shape: Optional[Tuple[int, int]] = None, + ) -> Dict: raise NotImplementedError @staticmethod diff --git a/nerfactory/data/image_dataset.py b/nerfactory/data/image_dataset.py index da2792229..deb511fb7 100644 --- a/nerfactory/data/image_dataset.py +++ b/nerfactory/data/image_dataset.py @@ -87,7 +87,7 @@ class ImageDataset(Dataset): assert image.shape[2] in [3, 4], f"Image shape of {image.shape} is in correct." return image - def get_image(self, image_idx: int): + def get_image(self, image_idx: int) -> TensorType["image_height", "image_width", "num_channels"]: """Returns a 3 channel image.""" image = torch.from_numpy(self.get_numpy_image(image_idx).astype("float32") / 255.0) if self.alpha_color is not None and image.shape[-1] == 4: diff --git a/nerfactory/graphs/base.py b/nerfactory/graphs/base.py index e9512dd68..6c497c455 100644 --- a/nerfactory/graphs/base.py +++ b/nerfactory/graphs/base.py @@ -86,9 +86,6 @@ class Graph(nn.Module): self.populate_fields() self.populate_misc_modules() # populate the modules self.callbacks = None - # variable for visualizer to fetch TODO(figure out if there is cleaner way to do this) - self.vis_outputs = None - self.default_output_name = None # to keep track of which device the nn.Module is on self.device_indicator_param = nn.Parameter(torch.empty(0)) diff --git a/nerfactory/viewer/app/.env.development b/nerfactory/viewer/app/.env.development index fd2617ea9..358cb9e29 100644 --- a/nerfactory/viewer/app/.env.development +++ b/nerfactory/viewer/app/.env.development @@ -1,4 +1,5 @@ BROWSER=none FAST_REFRESH=false HOST=localhost -PORT=4000 \ No newline at end of file +PORT=4000 +ESLINT_NO_DEV_ERRORS=true \ No newline at end of file diff --git a/nerfactory/viewer/app/.eslintrc.json b/nerfactory/viewer/app/.eslintrc.json new file mode 100644 index 000000000..8c21d1348 --- /dev/null +++ b/nerfactory/viewer/app/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "eslint:recommended", + "plugin:react/recommended", + "airbnb", + "prettier" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaFeatures": { + "jsx": true + }, + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": [ + "react", + "@typescript-eslint" + ], + "rules": { + "arrow-body-style": "off", + "camelcase": "off", + "import/prefer-default-export": "off", + "no-alert": "off", + "no-console": "off", + "prefer-destructuring": "off", + "react/destructuring-assignment": "off" + } +} \ No newline at end of file diff --git a/nerfactory/viewer/app/package.json b/nerfactory/viewer/app/package.json index ad5827f0a..03739ef83 100644 --- a/nerfactory/viewer/app/package.json +++ b/nerfactory/viewer/app/package.json @@ -4,28 +4,37 @@ "version": "0.0.0", "private": true, "dependencies": { + "@reduxjs/toolkit": "^1.8.3", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", "@testing-library/user-event": "^14.2.0", "ccapture.js": "github:aceslowman/ccapture.js#npm-fix", + "classnames": "^2.3.1", "dat.gui": "^0.7.9", + "eslint-config-prettier": "^8.5.0", "leva": "^0.9.29", "msgpack-lite": "^0.1.26", + "prop-types": "^15.8.1", "react": "^18.1.0", "react-dom": "^18.1.0", - "react-scripts": "^5.0.1", + "react-redux": "^8.0.2", + "redux": "^4.2.0", "socket.io-client": "^4.5.1", "three": "^0.142.0", "three-wtm": "^1.0", "websocket": "^1.0.34", - "wwobjloader2": "^4.0" + "wwobjloader2": "^4.0", + "electron": "^19.0.4", + "electron-is-dev": "^2.0.0" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", - "electron": "electron ." + "electron": "electron .", + "lint": "eslint --ext .js,.jsx .", + "lint:fix": "eslint --fix --ext .js,.jsx ." }, "eslintConfig": { "extends": "react-app" @@ -47,10 +56,20 @@ "license": "ISC", "description": "", "devDependencies": { + "@svgr/webpack": "^6.2.1", + "prettier": "2.7.1", "concurrently": "^7.2.1", - "electron": "^19.0.4", - "electron-is-dev": "^2.0.0", + "eslint": "^8.2.0", + "eslint-config-airbnb": "19.0.4", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.28.0", + "eslint-plugin-react-hooks": "^4.3.0", + "react-scripts": "^5.0.1", "typescript": "^4.7.3", "wait-on": "^6.0.1" + }, + "resolutions": { + "@svgr/webpack": "$@svgr/webpack" } } diff --git a/nerfactory/viewer/app/public/favicon.ico b/nerfactory/viewer/app/public/favicon.ico deleted file mode 100644 index bcd5dfd67..000000000 Binary files a/nerfactory/viewer/app/public/favicon.ico and /dev/null differ diff --git a/nerfactory/viewer/app/public/favicon.png b/nerfactory/viewer/app/public/favicon.png new file mode 100644 index 000000000..559199e18 Binary files /dev/null and b/nerfactory/viewer/app/public/favicon.png differ diff --git a/nerfactory/viewer/app/public/index.html b/nerfactory/viewer/app/public/index.html index 9c12b199a..b7227e088 100644 --- a/nerfactory/viewer/app/public/index.html +++ b/nerfactory/viewer/app/public/index.html @@ -2,7 +2,7 @@ - + - nerfactory Viewer + Nerfviewer diff --git a/nerfactory/viewer/app/public/logo192.png b/nerfactory/viewer/app/public/logo192.png deleted file mode 100644 index fc44b0a37..000000000 Binary files a/nerfactory/viewer/app/public/logo192.png and /dev/null differ diff --git a/nerfactory/viewer/app/public/logo512.png b/nerfactory/viewer/app/public/logo512.png deleted file mode 100644 index a4e47a654..000000000 Binary files a/nerfactory/viewer/app/public/logo512.png and /dev/null differ diff --git a/nerfactory/viewer/app/public/manifest.json b/nerfactory/viewer/app/public/manifest.json index 080d6c77a..f9051fe71 100644 --- a/nerfactory/viewer/app/public/manifest.json +++ b/nerfactory/viewer/app/public/manifest.json @@ -1,23 +1,6 @@ { "short_name": "React App", "name": "Create React App Sample", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - }, - { - "src": "logo192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "logo512.png", - "type": "image/png", - "sizes": "512x512" - } - ], "start_url": ".", "display": "standalone", "theme_color": "#000000", diff --git a/nerfactory/viewer/app/src/App.css b/nerfactory/viewer/app/src/App.css new file mode 100644 index 000000000..ec2585e8c --- /dev/null +++ b/nerfactory/viewer/app/src/App.css @@ -0,0 +1,13 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} diff --git a/nerfactory/viewer/app/src/App.jsx b/nerfactory/viewer/app/src/App.jsx new file mode 100644 index 000000000..8c59f9410 --- /dev/null +++ b/nerfactory/viewer/app/src/App.jsx @@ -0,0 +1,18 @@ +import './App.css'; + +import React from 'react'; +import { RenderControls } from './modules/ConfigPanel/ConfigPanel'; + +import SetupScene from './modules/Scene/Scene'; +import ViewerWindow from './modules/ViewerWindow/ViewerWindow'; + +export default function App() { + const scene = SetupScene(); + + return ( +
+ + +
+ ); +} diff --git a/nerfactory/viewer/app/src/ExtensibleObjectLoader.js b/nerfactory/viewer/app/src/ExtensibleObjectLoader.js deleted file mode 100644 index 01346a452..000000000 --- a/nerfactory/viewer/app/src/ExtensibleObjectLoader.js +++ /dev/null @@ -1,282 +0,0 @@ -import * as THREE from "three"; -import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader.js"; -import { ColladaLoader } from "three/examples/jsm/loaders/ColladaLoader.js"; -import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader.js"; -import { MtlObjBridge } from "wwobjloader2"; -import { STLLoader } from "three/examples/jsm/loaders/STLLoader.js"; -import { PLYLoader } from "three/examples/jsm/loaders/PLYLoader.js"; -import * as BufferGeometryUtils from "three/examples/jsm/utils/BufferGeometryUtils.js"; - -// Merges a hierarchy of collada mesh geometries into a single -// `BufferGeometry` object: -// * A new merged `BufferGeometry` if the input contains meshes -// * empty `BufferGeometry` otherwise -function merge_geometries(object, preserve_materials = false) { - let materials = []; - let geometries = []; - let root_transform = object.matrix.clone(); - - function collectGeometries(node, parent_transform) { - let transform = parent_transform.clone().multiply(node.matrix); - if (node.type === "Mesh") { - node.geometry.applyMatrix(transform); - geometries.push(node.geometry); - materials.push(node.material); - } - for (let child of node.children) { - collectGeometries(child, transform); - } - } - - collectGeometries(object, root_transform); - let result = null; - if (geometries.length === 1) { - result = geometries[0]; - if (preserve_materials) { - result.material = materials[0]; - } - } else if (geometries.length > 1) { - result = BufferGeometryUtils.mergeBufferGeometries(geometries, true); - if (preserve_materials) { - result.material = materials; - } - } else { - result = new THREE.BufferGeometry(); - } - return result; -} - -// Handler for special texture types that we want to support -// in addition to whatever three.js supports. This function -// takes a json object representing a single texture, and should -// return either: -// * A new `THREE.Texture` if that json represents a special texture -// * `null` otherwise -function handle_special_texture(json) { - if (json.type === "_text") { - let canvas = document.createElement("canvas"); - // canvas width and height should be in the power of 2; otherwise although - // the page usually loads successfully, WebGL does complain/warn - canvas.width = 256; - canvas.height = 256; - let ctx = canvas.getContext("2d"); - ctx.textAlign = "center"; - let font_size = json.font_size; - // auto-resing the font_size to fit in the canvas - ctx.font = font_size + "px " + json.font_face; - while (ctx.measureText(json.text).width > canvas.width) { - font_size--; - ctx.font = font_size + "px " + json.font_face; - } - ctx.fillText(json.text, canvas.width / 2, canvas.height / 2); - let canvas_texture = new THREE.CanvasTexture(canvas); - canvas_texture.uuid = json.uuid; - return canvas_texture; - } else { - return null; - } -} - -// Handler for special geometry types that we want to support -// in addition to whatever three.js supports. This function -// takes a json object representing a single geometry, and should -// return either: -// * A new `THREE.Mesh` if that json represents a special geometry -// * `null` otherwise -function handle_special_geometry(geom) { - if (geom.type === "_meshfile") { - console.warn( - "_meshfile is deprecated. Please use _meshfile_geometry for geometries and _meshfile_object for objects with geometry and material" - ); - geom.type = "_meshfile_geometry"; - } - if (geom.type === "_meshfile_geometry") { - if (geom.format === "obj") { - let loader = new OBJLoader(); - let obj = loader.parse(geom.data + "\n"); - let loaded_geom = merge_geometries(obj); - loaded_geom.uuid = geom.uuid; - return loaded_geom; - } else if (geom.format === "dae") { - let loader = new ColladaLoader(); - let obj = loader.parse(geom.data); - let result = merge_geometries(obj.scene); - result.uuid = geom.uuid; - return result; - } else if (geom.format === "stl") { - let loader = new STLLoader(); - let loaded_geom = loader.parse(geom.data.buffer); - loaded_geom.uuid = geom.uuid; - return loaded_geom; - } else if (geom.format === "ply") { - let loader = new PLYLoader(); - let loaded_geom = loader.parse(geom.data.buffer); - loaded_geom.uuid = geom.uuid; - return loaded_geom; - } else { - console.error("Unsupported mesh type:", geom); - return null; - } - } - return null; -} - -// The ExtensibleObjectLoader extends the THREE.ObjectLoader -// interface, while providing some hooks for us to perform some -// custom loading for things other than three.js native JSON. -// -// We currently use this class to support some extensions to -// three.js JSON for objects which are easy to construct in -// javascript but hard to construct in Python and/or Julia. -// For example, we perform the following transformations: -// -// * Converting "_meshfile" geometries into actual meshes -// using the THREE.js native mesh loaders -// * Converting "_text" textures into text by drawing the -// requested text onto a canvas. -export class ExtensibleObjectLoader extends THREE.ObjectLoader { - delegate(special_handler, base_handler, json, additional_objects) { - let result = {}; - if (json === undefined) { - return result; - } - let remaining_json = []; - for (let data of json) { - let x = special_handler(data); - if (x !== null) { - result[x.uuid] = x; - } else { - remaining_json.push(data); - } - } - return Object.assign( - result, - base_handler(remaining_json, additional_objects) - ); - } - - parseTextures(json, images) { - return this.delegate( - handle_special_texture, - super.parseTextures, - json, - images - ); - } - - parseGeometries(json, shapes) { - return this.delegate( - handle_special_geometry, - super.parseGeometries, - json, - shapes - ); - } - - parseObject(json, geometries, materials) { - if (json.type === "_meshfile_object") { - let geometry; - let material; - let manager = new THREE.LoadingManager(); - let path = - json.url === undefined - ? undefined - : THREE.LoaderUtils.extractUrlBase(json.url); - manager.setURLModifier((url) => { - if (json.resources[url] !== undefined) { - return json.resources[url]; - } - return url; - }); - if (json.format === "obj") { - let loader = new OBJLoader(manager); - if (json.mtl_library) { - let mtl_loader = new MTLLoader(manager); - let mtl_parse_result = mtl_loader.parse(json.mtl_library + "\n", ""); - let materials = - MtlObjBridge.addMaterialsFromMtlLoader(mtl_parse_result); - loader.setMaterials(materials); - this.onTextureLoad(); - } - let obj = loader.parse(json.data + "\n", path); - geometry = merge_geometries(obj, true); - geometry.uuid = json.uuid; - material = geometry.material; - } else if (json.format === "dae") { - let loader = new ColladaLoader(manager); - loader.onTextureLoad = this.onTextureLoad; - let obj = loader.parse(json.data, path); - geometry = merge_geometries(obj.scene, true); - geometry.uuid = json.uuid; - material = geometry.material; - } else if (json.format === "stl") { - let loader = new STLLoader(); - geometry = loader.parse(json.data.buffer, path); - geometry.uuid = json.uuid; - material = geometry.material; - } else { - console.error("Unsupported mesh type:", json); - return null; - } - let object = new THREE.Mesh(geometry, material); - - // Copied from ObjectLoader - object.uuid = json.uuid; - - if (json.name !== undefined) object.name = json.name; - - if (json.matrix !== undefined) { - object.matrix.fromArray(json.matrix); - - if (json.matrixAutoUpdate !== undefined) - object.matrixAutoUpdate = json.matrixAutoUpdate; - if (object.matrixAutoUpdate) - object.matrix.decompose( - object.position, - object.quaternion, - object.scale - ); - } else { - if (json.position !== undefined) - object.position.fromArray(json.position); - if (json.rotation !== undefined) - object.rotation.fromArray(json.rotation); - if (json.quaternion !== undefined) - object.quaternion.fromArray(json.quaternion); - if (json.scale !== undefined) object.scale.fromArray(json.scale); - } - - if (json.castShadow !== undefined) object.castShadow = json.castShadow; - if (json.receiveShadow !== undefined) - object.receiveShadow = json.receiveShadow; - - if (json.shadow) { - if (json.shadow.bias !== undefined) - object.shadow.bias = json.shadow.bias; - if (json.shadow.radius !== undefined) - object.shadow.radius = json.shadow.radius; - if (json.shadow.mapSize !== undefined) - object.shadow.mapSize.fromArray(json.shadow.mapSize); - if (json.shadow.camera !== undefined) - object.shadow.camera = this.parseObject(json.shadow.camera); - } - - if (json.visible !== undefined) object.visible = json.visible; - if (json.frustumCulled !== undefined) - object.frustumCulled = json.frustumCulled; - if (json.renderOrder !== undefined) object.renderOrder = json.renderOrder; - if (json.userjson !== undefined) object.userjson = json.userData; - if (json.layers !== undefined) object.layers.mask = json.layers; - - return object; - } else if (json.type === "CameraHelper") { - console.log("processing CameraHelper"); - console.log(json); - console.log(geometries); - console.log(materials); - return super.parseObject(json, geometries, materials); - } else { - return super.parseObject(json, geometries, materials); - } - } -} diff --git a/nerfactory/viewer/app/src/SceneNode.js b/nerfactory/viewer/app/src/SceneNode.js index 293def860..edfa23d58 100644 --- a/nerfactory/viewer/app/src/SceneNode.js +++ b/nerfactory/viewer/app/src/SceneNode.js @@ -1,12 +1,14 @@ -import * as THREE from "three"; +/* eslint-disable no-underscore-dangle */ +/* eslint-disable no-restricted-syntax */ +import * as THREE from 'three'; -const dat = require("dat.gui"); +const dat = require('dat.gui'); function remove_folders(gui) { - for (let name of Object.keys(gui.__folders)) { - let folder = gui.__folders[name]; + for (const name of Object.keys(gui.__folders)) { + const folder = gui.__folders[name]; remove_folders(folder); - dat.dom.dom.unbind(window, "resize", folder.__resizeHandler); + dat.dom.dom.unbind(window, 'resize', folder.__resizeHandler); gui.removeFolder(folder); } } @@ -20,7 +22,7 @@ function dispose(object) { } if (object.material) { if (Array.isArray(object.material)) { - for (let material of object.material) { + for (const material of object.material) { if (material.map) { material.map.dispose(); } @@ -35,42 +37,41 @@ function dispose(object) { } } -export class SceneNode { +export default class SceneNode { constructor(object, folder) { this.object = object; this.folder = folder; this.children = {}; this.create_controls(); - for (let c of this.object.children) { + for (const c of this.object.children) { this.add_child(c); } } add_child(object) { - let f = this.folder.addFolder(object.name); - let node = new SceneNode(object, f); + const f = this.folder.addFolder(object.name); + const node = new SceneNode(object, f); this.children[object.name] = node; return node; } create_child(name) { - let obj = new THREE.Group(); + const obj = new THREE.Group(); obj.name = name; this.object.add(obj); return this.add_child(obj); } find(path) { - if (path.length == 0) { + if (path.length === 0) { return this; - } else { - let name = path[0]; - let child = this.children[name]; - if (child === undefined) { - child = this.create_child(name); - } - return child.find(path.slice(1)); } + const name = path[0]; + let child = this.children[name]; + if (child === undefined) { + child = this.create_child(name); + } + return child.find(path.slice(1)); } create_controls() { @@ -79,19 +80,19 @@ export class SceneNode { } this.vis_controller = new dat.controllers.BooleanController( this.object, - "visible" + 'visible', ); this.folder.domElement.prepend(this.vis_controller.domElement); - this.vis_controller.domElement.style.height = "0"; - this.vis_controller.domElement.style.float = "right"; + this.vis_controller.domElement.style.height = '0'; + this.vis_controller.domElement.style.float = 'right'; } set_property(property, value) { - if (property === "position") { + if (property === 'position') { this.object.position.set(value[0], value[1], value[2]); - } else if (property === "quaternion") { + } else if (property === 'quaternion') { this.object.quaternion.set(value[0], value[1], value[2], value[3]); - } else if (property === "scale") { + } else if (property === 'scale') { this.object.scale.set(value[0], value[1], value[2]); } else { this.object[property] = value; @@ -100,17 +101,17 @@ export class SceneNode { } set_transform(matrix) { - let mat = new THREE.Matrix4(); + const mat = new THREE.Matrix4(); mat.fromArray(matrix); mat.decompose( this.object.position, this.object.quaternion, - this.object.scale + this.object.scale, ); } set_object(object) { - let parent = this.object.parent; + const parent = this.object.parent; this.dispose_recursive(); this.object.parent.remove(this.object); this.object = object; @@ -119,19 +120,19 @@ export class SceneNode { } dispose_recursive() { - for (let name of Object.keys(this.children)) { + for (const name of Object.keys(this.children)) { this.children[name].dispose_recursive(); } dispose(this.object); } delete(path) { - if (path.length == 0) { + if (path.length === 0) { console.error("Can't delete an empty path"); } else { - let parent = this.find(path.slice(0, path.length - 1)); - let name = path[path.length - 1]; - let child = parent.children[name]; + const parent = this.find(path.slice(0, path.length - 1)); + const name = path[path.length - 1]; + const child = parent.children[name]; if (child !== undefined) { child.dispose_recursive(); parent.object.remove(child.object); diff --git a/nerfactory/viewer/app/src/Viewer.jsx b/nerfactory/viewer/app/src/Viewer.jsx deleted file mode 100644 index 0e25cfbee..000000000 --- a/nerfactory/viewer/app/src/Viewer.jsx +++ /dev/null @@ -1,461 +0,0 @@ -import React, { Component } from "react"; -import * as THREE from "three"; -import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; -import Stats from "three/examples/jsm/libs/stats.module.js"; -import { GUI } from "dat.gui"; -import { split_path } from "./utils"; -import { SceneNode } from "./SceneNode"; -import { ExtensibleObjectLoader } from "./ExtensibleObjectLoader"; - -function websocket_endpoint_from_url(url) { - let endpoint = url.split("?").pop(); - if (endpoint == "") { - let message = - "Please set the websocket endpoint. E.g., a correct URL may be: http://localhost:4000?localhost:8051"; - window.alert(message); - return null; - } - return endpoint; -} - -function findCameraObjectUnderObject3D(object) { - // seach the tree for the camera - if (object instanceof THREE.Camera) { - return object; - } - for (let name of Object.keys(object.children)) { - return findCameraObjectUnderObject3D(object.children[name]); - } - return null; -} - -var msgpack = require("msgpack-lite"); - -export class ViewerState extends Component { - constructor(props) { - super(props); - this.state = { - scene: null, - controls: null, - renderer: null, - gui: null, - scene_tree: null, - viewport_width: null, - viewport_height: null, - }; - this.update = this.update.bind(this); - this.set_object = this.set_object.bind(this); - this.handle_command = this.handle_command.bind(this); - this.threejs_ref = React.createRef(); - this.props = props; - } - - handleResize() { - this.state.viewport_width = this.getViewportWidth(); - this.state.viewport_height = this.getViewportHeight(); - this.state.camera_main.aspect = - this.state.viewport_width / this.state.viewport_height; - this.state.camera_main.updateProjectionMatrix(); - this.state.renderer_main.setSize( - this.state.viewport_width, - this.state.viewport_height - ); - } - - update() { - requestAnimationFrame(this.update); - this.handleResize(); - let camera_main = findCameraObjectUnderObject3D( - this.state.scene_tree.find(["Cameras", "Main Camera"]).object - ); - camera_main.updateProjectionMatrix(); - if (this.state.controls_main != null) { - this.state.controls_main.update(); - } - this.state.renderer_main.render(this.state.scene, camera_main); - this.send_camera_over_websocket(); - } - - send_camera_over_websocket() { - /* update the camera information in the python server - if the websocket is connected */ - // console.log("send_camera_over_websocket"); - if (this.state.websocket.readyState === WebSocket.OPEN) { - // update the camera information in the python server - let camera_main = findCameraObjectUnderObject3D( - this.state.scene_tree.find(["Cameras", "Main Camera"]).object - ); - // console.log(camera_main.toJSON()); - let cmd = "set_object"; - let path = "Cameras/Main Camera"; - let data = { - type: cmd, - path: path, - object: camera_main.toJSON(), - }; - let message = msgpack.encode(data); - this.state.websocket.send(message); - } - } - - send_training_state_over_websocket(value) { - if (this.state.websocket.readyState === WebSocket.OPEN) { - let cmd = "set_training_state"; - let path = "Training State"; - let data = { - type: cmd, - path: path, - training_state: value, - }; - let message = msgpack.encode(data); - this.state.websocket.send(message); - } - } - - send_output_type_over_websocket(value) { - /* update the output option in the python server - if the user changes selection */ - if (this.state.websocket.readyState === WebSocket.OPEN) { - let cmd = "set_output_type"; - let path = "Output Type"; - let data = { - type: cmd, - path: path, - output_type: value, - }; - let message = msgpack.encode(data); - this.state.websocket.send(message); - } - } - - send_min_resolution_over_websocket(value) { - if (this.state.websocket.readyState === WebSocket.OPEN) { - let cmd = "set_min_resolution"; - let path = "Min Resolution"; - let data = { - type: cmd, - path: path, - min_resolution: value, - }; - let message = msgpack.encode(data); - this.state.websocket.send(message); - } - } - - send_max_resolution_over_websocket(value) { - if (this.state.websocket.readyState === WebSocket.OPEN) { - let cmd = "set_max_resolution"; - let path = "Max Resolution"; - let data = { - type: cmd, - path: path, - max_resolution: value, - }; - let message = msgpack.encode(data); - this.state.websocket.send(message); - } - } - - set_object(path, object) { - if (!(object instanceof THREE.Camera)) { - this.state.scene_tree.find(path.concat([""])).set_object(object); - } - } - - set_object_from_json(path, object_json) { - let loader = new ExtensibleObjectLoader(); - loader.parse(object_json, (obj) => { - if ( - obj.geometry !== undefined && - obj.geometry.type === "BufferGeometry" - ) { - if ( - obj.geometry.attributes.normal === undefined || - obj.geometry.attributes.normal.count === 0 - ) { - obj.geometry.computeVertexNormals(); - } - } - obj.castShadow = true; - obj.receiveShadow = true; - this.set_object(path, obj); - }); - } - - set_transform(path, matrix) { - this.state.scene_tree.find(path).set_transform(matrix); - } - - delete_path(path) { - if (path.length === 0) { - console.error("Deleting the entire scene is not implemented."); - } else { - this.state.scene_tree.delete(path); - } - } - - set_property(path, property, value) { - this.state.scene_tree.find(path).set_property(property, value); - // TODO(ethan): handle this issue - if (path[0] === "Background") { - // The background is not an Object3d, so needs a little help. - this.state.scene_tree.find(path).on_update(); - } - } - - handle_command(cmd) { - // convert binary serialization format back to JSON - cmd = msgpack.decode(new Uint8Array(cmd)); - // console.log(cmd); - - // three js scene commands - if (cmd.type === "set_object") { - let path = split_path(cmd.path); - this.set_object_from_json(path, cmd.object); - } else if (cmd.type === "set_transform") { - let path = split_path(cmd.path); - this.set_transform(path, cmd.matrix); - } else if (cmd.type === "delete") { - let path = split_path(cmd.path); - this.delete_path(path); - } else if (cmd.type === "set_property") { - let path = split_path(cmd.path); - this.set_property(path, cmd.property, cmd.value); - } else if (cmd.type == "set_output_options") { - this.props.setOutputOptions(cmd.output_options); - this.props.setControls({ output_options: 1 }); - } - // web rtc commands - else if (cmd.type === "answer") { - let answer = cmd.data; - this.state.pc.setRemoteDescription(answer); - } - - // possibly update the camera information in the python server - // this.send_camera_over_websocket(); - } - - getViewportWidth() { - return window.innerWidth - (window.innerWidth % 2); - } - - getViewportHeight() { - return window.innerHeight; - } - - setupWebRTC() { - console.log("setting up WebRTC"); - // The iceServers config comes from the following URL: - // https://www.metered.ca/tools/openrelay/ - this.state.pc = new RTCPeerConnection({ - iceServers: [ - { - urls: "stun:openrelay.metered.ca:80", - }, - { - urls: "turn:openrelay.metered.ca:80", - username: "openrelayproject", - credential: "openrelayproject", - }, - { - urls: "turn:openrelay.metered.ca:443", - username: "openrelayproject", - credential: "openrelayproject", - }, - { - urls: "turn:openrelay.metered.ca:443?transport=tcp", - username: "openrelayproject", - credential: "openrelayproject", - }, - ], - }); - - // connect video - this.state.pc.addEventListener("track", function (evt) { - if (evt.track.kind == "video") { - console.log("setting event stream"); - document.getElementById("WebRTCVideo-video").srcObject = evt.streams[0]; - } - }); - this.state.pc.addTransceiver("video", { direction: "recvonly" }); - - this.state.pc - .createOffer() - .then((offer) => { - return this.state.pc.setLocalDescription(offer); - }) - .then(() => { - // wait for ICE gathering to complete - return new Promise((resolve) => { - if (this.state.pc.iceGatheringState === "complete") { - resolve(); - } else { - var checkState = () => { - if (this.state.pc.iceGatheringState === "complete") { - this.state.pc.removeEventListener( - "icegatheringstatechange", - checkState - ); - resolve(); - } - }; - this.state.pc.addEventListener( - "icegatheringstatechange", - checkState - ); - } - }); - }) - .then(() => { - var offer = this.state.pc.localDescription; - console.log("sending the offer"); - let cmd = "offer"; - let path = ""; - let data = { - type: cmd, - path: path, - data: { - sdp: offer.sdp, - type: offer.type, - }, - }; - let message = msgpack.encode(data); - this.state.websocket.send(message); - }); - } - - componentDidMount() { - // Open the websocket - let endpoint = websocket_endpoint_from_url(window.location.href); - this.state.websocket = new WebSocket("ws://" + endpoint + "/"); - this.state.websocket.onopen = () => { - console.log("websocket connected"); - this.setupWebRTC(); - }; - this.state.websocket.binaryType = "arraybuffer"; - // 'command' for updates to Viewer - this.state.websocket.onmessage = (cmd) => this.handle_command(cmd.data); - this.state.websocket.onclose = function (evt) { - console.log("onclose:", evt); - }; - - // Get size of screen - this.state.viewport_width = this.getViewportWidth(); - this.state.viewport_height = this.getViewportHeight(); - - // Scene - this.state.scene = new THREE.Scene(); - - // GUI - this.state.gui = new GUI(); - let scene_folder = this.state.gui.addFolder("Scene"); - scene_folder.open(); - this.state.scene_tree = new SceneNode(this.state.scene, scene_folder); - - // Renderer main - this.state.renderer_main = new THREE.WebGLRenderer({ - antialias: true, - alpha: true, - }); - this.state.renderer_main.setPixelRatio(window.devicePixelRatio); - this.state.renderer_main.setSize( - this.state.viewport_width, - this.state.viewport_height - ); - this.state.renderer_main.domElement.style.border = "1px solid black"; - - let domElement = document.getElementById("canvas-container-main"); - domElement.append(this.state.renderer_main.domElement); - - // Camera main - this.state.camera_main = new THREE.PerspectiveCamera( - 120, - this.state.viewport_width / this.state.viewport_height, - 0.01, - 100 - ); - this.state.camera_main.position.x = 5; - this.state.camera_main.position.y = -5; - this.state.camera_main.position.z = 5; - this.state.camera_main.up = new THREE.Vector3(0, 0, 1); - - this.state.controls_main = new OrbitControls( - this.state.camera_main, - this.state.renderer_main.domElement - ); - this.state.controls_main.rotateSpeed = 2.0; - this.state.controls_main.zoomSpeed = 0.3; - this.state.controls_main.panSpeed = 0.2; - this.state.controls_main.target.set(0, 0, 0); // focus point of the controls - this.state.controls_main.autoRotate = false; - this.state.controls_main.enableDamping = true; - this.state.controls_main.dampingFactor = 1.0; - this.state.controls_main.update(); - - let path = ["Cameras", "Main Camera"]; - this.state.scene_tree - .find(path.concat([""])) - .set_object(this.state.camera_main); - - // Axes display - let axes = new THREE.AxesHelper(5); - this.set_object(["Axes"], axes); - - // Grid - let grid = new THREE.GridHelper(20, 20); - grid.rotateX(Math.PI / 2); // now on xy plane - this.set_object(["Grid"], grid); - - // Lights - let color = 0xffffff; - let intensity = 1; - let light = new THREE.AmbientLight(color, intensity); - this.set_object(["Light"], light); - - // Stats - var stats = new Stats(); - let domElement_stats = document.getElementById("canvas-container-main"); - domElement_stats.append(stats.dom); - - this.update(); - } - - componentDidUpdate(prevProps, prevState) { - // Pause training - if (prevProps.paused !== this.props.paused) { - this.send_training_state_over_websocket(this.props.paused); - } - - // Choose render type - if (prevProps.output_options !== this.props.output_options) { - console.log(this.props.output_options); - this.send_output_type_over_websocket(this.props.output_options); - } - - // Set minimum render resolution - if (prevProps.min_resolution !== this.props.min_resolution) { - this.send_min_resolution_over_websocket( - this.props.min_resolution.slice(0, -2) - ); - } - - // Set maximum render resolution - if (prevProps.max_resolution !== this.props.max_resolution) { - this.send_max_resolution_over_websocket( - this.props.max_resolution.slice(0, -2) - ); - } - } - - render() { - return ( -
-
- -
-
-
-
- ); - } -} diff --git a/nerfactory/viewer/app/src/controlPanel.js b/nerfactory/viewer/app/src/controlPanel.js deleted file mode 100644 index b6f33a214..000000000 --- a/nerfactory/viewer/app/src/controlPanel.js +++ /dev/null @@ -1,68 +0,0 @@ -import React from "react"; -import { Leva, button, buttonGroup, useControls } from "leva"; - -export function PanelConfig() { - let params = { titleBar: false }; - let panel = ( -
- -
- ); - return panel; -} - -export function RenderControls() { - const [outputOptions, setOutputOptions] = React.useState([]); - const [paused, setPaused] = React.useState(false); - - const toggle = () => { - setPaused((current) => !current); - }; - - const [controls, setControls] = useControls( - "Render Options", - () => ({ - "Resume Training": button(toggle, { - disabled: !paused, - }), - "Pause Training": button(toggle, { - disabled: paused, - }), - output_options: { - label: "Output Render", - options: outputOptions, - }, - min_resolution: { - label: "Min Res.", - value: 50, - min: 10, - max: 100, - step: 1, - suffix: "px", - }, - " ": buttonGroup({ - "25px": () => setControls({ min_resolution: 25 }), - "50px": () => setControls({ min_resolution: 50 }), - "75px": () => setControls({ min_resolution: 75 }), - "100px": () => setControls({ min_resolution: 100 }), - }), - max_resolution: { - label: "Max Res.", - min: 10, - max: 2048, - step: 1, - value: 512, - suffix: "px", - }, - " ": buttonGroup({ - "128px": () => setControls({ max_resolution: 128 }), - "256px": () => setControls({ max_resolution: 256 }), - "512px": () => setControls({ max_resolution: 512 }), - "1024px": () => setControls({ max_resolution: 1024 }), - }), - }), - [outputOptions, paused] - ); - - return [controls, paused, setControls, setOutputOptions]; -} diff --git a/nerfactory/viewer/app/src/index.css b/nerfactory/viewer/app/src/index.css deleted file mode 100644 index 80277e1f3..000000000 --- a/nerfactory/viewer/app/src/index.css +++ /dev/null @@ -1,43 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", - monospace; -} - -.BackgroundDiv, -.WebRTCVideo { - position: absolute; - top: 0px; - right: 0px; - bottom: 0px; - left: 0px; -} - -#BackgroundDiv-video, -#WebRTCVideo-video { - width: 100%; - height: 100%; - object-fit: fill; -} - -#canvas-container-main { - position: absolute; - top: 0px; - right: 0px; - bottom: 0px; - left: 0px; -} - -#canvas-container-main canvas { - position: relative; - height: 100%; - width: 100%; -} diff --git a/nerfactory/viewer/app/src/index.js b/nerfactory/viewer/app/src/index.js deleted file mode 100644 index 760af9fc2..000000000 --- a/nerfactory/viewer/app/src/index.js +++ /dev/null @@ -1,32 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import "./index.css"; -import * as serviceWorker from "./serviceWorker"; -import { ViewerState } from "./Viewer"; -import { PanelConfig, RenderControls } from "./controlPanel"; - -function App() { - let [controls, paused, setControls, setOutputOptions] = RenderControls(); - - console.log(controls); - return ( -
- - - -
- ); -} - -const root = ReactDOM.createRoot(document.getElementById("root")); -root.render(); - -// If you want your app to work offline and load faster, you can change -// unregister() to register() below. Note this comes with some pitfalls. -// Learn more about service workers: https://bit.ly/CRA-PWA -serviceWorker.unregister(); diff --git a/nerfactory/viewer/app/src/index.jsx b/nerfactory/viewer/app/src/index.jsx new file mode 100644 index 000000000..924676521 --- /dev/null +++ b/nerfactory/viewer/app/src/index.jsx @@ -0,0 +1,15 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { Provider } from 'react-redux'; +import App from './App'; +import WebSocketProvider from './modules/WebSocket/WebSocket'; +import store from './store'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + + + , +); \ No newline at end of file diff --git a/nerfactory/viewer/app/src/logo.svg b/nerfactory/viewer/app/src/logo.svg deleted file mode 100644 index 6b60c1042..000000000 --- a/nerfactory/viewer/app/src/logo.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/nerfactory/viewer/app/src/modules/ConfigPanel/ConfigPanel.css b/nerfactory/viewer/app/src/modules/ConfigPanel/ConfigPanel.css new file mode 100644 index 000000000..e69de29bb diff --git a/nerfactory/viewer/app/src/modules/ConfigPanel/ConfigPanel.jsx b/nerfactory/viewer/app/src/modules/ConfigPanel/ConfigPanel.jsx new file mode 100644 index 000000000..ccf97f1d7 --- /dev/null +++ b/nerfactory/viewer/app/src/modules/ConfigPanel/ConfigPanel.jsx @@ -0,0 +1,252 @@ +import { button, buttonGroup, useControls } from 'leva'; +import { useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; + +import { WebSocketContext } from '../WebSocket/WebSocket'; + +const msgpack = require('msgpack-lite'); + +export function RenderControls() { + // connection status indicators + const websocket = useContext(WebSocketContext).socket; + const isWebsocketConnected = useSelector( + (state) => state.websocketState.isConnected, + ); + const isWebrtcConnected = useSelector( + (state) => state.webrtcState.isConnected, + ); + const isTraining = useSelector((state) => state.renderingState.isTraining); + const outputOptions = useSelector( + (state) => state.renderingState.output_options, + ); + const outputChoice = useSelector( + (state) => state.renderingState.output_choice, + ); + const min_resolution = useSelector( + (state) => state.renderingState.minResolution, + ); + const max_resolution = useSelector( + (state) => state.renderingState.maxResolution, + ); + const field_of_view = useSelector( + (state) => state.renderingState.field_of_view, + ); + + const dispatch = useDispatch(); + + const set_min_resolution = (value) => { + if (websocket.readyState === WebSocket.OPEN) { + dispatch({ + type: 'write', + path: 'renderingState/minResolution', + data: value, + }); + const cmd = 'write'; + const path = 'renderingState/minResolution'; + const data = { + type: cmd, + path, + data: value, + }; + const message = msgpack.encode(data); + websocket.send(message); + } + }; + + const set_max_resolution = (value) => { + if (websocket.readyState === WebSocket.OPEN) { + dispatch({ + type: 'write', + path: 'renderingState/maxResolution', + data: value, + }); + const cmd = 'write'; + const path = 'renderingState/maxResolution'; + const data = { + type: cmd, + path, + data: value, + }; + const message = msgpack.encode(data); + websocket.send(message); + } + }; + + const set_output_choice = (value) => { + if (websocket.readyState === WebSocket.OPEN) { + dispatch({ + type: 'write', + path: 'renderingState/output_choice', + data: value, + }); + const cmd = 'write'; + const path = 'renderingState/output_choice'; + const data = { + type: cmd, + path, + data: value, + }; + const message = msgpack.encode(data); + websocket.send(message); + } + }; + + const set_fov = (value) => { + if (websocket.readyState === WebSocket.OPEN) { + dispatch({ + type: 'write', + path: 'renderingState/field_of_view', + data: value, + }); + const cmd = 'write'; + const path = 'renderingState/field_of_view'; + const data = { + type: cmd, + path, + data: value, + }; + const message = msgpack.encode(data); + websocket.send(message); + } + }; + + const [, setControls] = useControls( + 'Rendering State', + () => ({ + // WebSocket isConnected + // button does nothing except be an indicator + 'WebSocket Connected': button(() => {}, { + disabled: !isWebsocketConnected, + }), + // webRTC isConnected + // button does nothing except be an indicator + 'WebRTC Connected': button(() => {}, { + disabled: !isWebrtcConnected, + }), + // isTraining + 'Pause Training': button( + () => { + // write locally to store + dispatch({ + type: 'write', + path: 'renderingState/isTraining', + data: false, + }); + // write to server + const cmd = 'write'; + const path = 'renderingState/isTraining'; + const data = { + type: cmd, + path, + data: false, + }; + const message = msgpack.encode(data); + websocket.send(message); + }, + { + disabled: !isTraining, + }, + ), + 'Resume Training': button( + () => { + // write locally to store + dispatch({ + type: 'write', + path: 'renderingState/isTraining', + data: true, + }); + // write to server + const cmd = 'write'; + const path = 'renderingState/isTraining'; + const data = { + type: cmd, + path, + data: true, + }; + const message = msgpack.encode(data); + websocket.send(message); + }, + { + disabled: isTraining, + }, + ), + // output_options + output_options: { + label: 'Output Render', + options: outputOptions, + value: outputChoice, + onChange: (v) => { + set_output_choice(v); + }, + }, + // resolution + min_resolution: { + label: 'Min Res.', + value: min_resolution, + min: 10, + max: 100, + step: 1, + onChange: (v) => { + set_min_resolution(v); + }, + }, + ' ': buttonGroup({ + '25px': () => setControls({ min_resolution: 25 }), + '50px': () => setControls({ min_resolution: 50 }), + '75px': () => setControls({ min_resolution: 75 }), + '100px': () => setControls({ min_resolution: 100 }), + }), + max_resolution: { + label: 'Max Res.', + value: max_resolution, + min: 10, + max: 2048, + step: 1, + onChange: (v) => { + set_max_resolution(v); + }, + }, + ' ': buttonGroup({ + '128px': () => setControls({ max_resolution: 128 }), + '256px': () => setControls({ max_resolution: 256 }), + '512px': () => setControls({ max_resolution: 512 }), + '1024px': () => setControls({ max_resolution: 1024 }), + }), + 'Camera FoV': { + value: field_of_view, + onChange: (v) => { + set_fov(v); + }, + }, + }), + [ + isWebsocketConnected, + isWebrtcConnected, + isTraining, + outputOptions, + outputChoice, + min_resolution, + max_resolution, + field_of_view, + ], + ); + + useEffect(() => { + setControls({ min_resolution }); + setControls({ max_resolution }); + setControls({ output_options: outputChoice }); + setControls({ 'Camera FoV': field_of_view }); + }, [ + setControls, + isWebsocketConnected, + isWebrtcConnected, + isTraining, + outputOptions, + outputChoice, + min_resolution, + max_resolution, + field_of_view, + ]); + + return null; +} diff --git a/nerfactory/viewer/app/src/modules/ConfigPanel/ConfigPanelSlice.js b/nerfactory/viewer/app/src/modules/ConfigPanel/ConfigPanelSlice.js new file mode 100644 index 000000000..30db097df --- /dev/null +++ b/nerfactory/viewer/app/src/modules/ConfigPanel/ConfigPanelSlice.js @@ -0,0 +1,6 @@ +// The function below is called a selector and allows us to select a value from +// the state. Selectors can also be defined inline where they're used instead of +// in the slice file. For example: `useSelector((state) => state.counter.value)` +export const selectTrainingState = (state) => + state.shared.rendering.training_state; +export const selectOutputOptions = (state) => state.shared.output_options; diff --git a/nerfactory/viewer/app/src/modules/Scene/Scene.css b/nerfactory/viewer/app/src/modules/Scene/Scene.css new file mode 100644 index 000000000..43ff9bf1a --- /dev/null +++ b/nerfactory/viewer/app/src/modules/Scene/Scene.css @@ -0,0 +1,5 @@ +#datgui { + position: absolute; + top: 2px; + left: 2px; +} diff --git a/nerfactory/viewer/app/src/modules/Scene/Scene.jsx b/nerfactory/viewer/app/src/modules/Scene/Scene.jsx new file mode 100644 index 000000000..4e5a1b891 --- /dev/null +++ b/nerfactory/viewer/app/src/modules/Scene/Scene.jsx @@ -0,0 +1,127 @@ +import './Scene.css'; + +/* eslint-disable no-restricted-syntax */ +import * as THREE from 'three'; + +import { useContext, useEffect } from 'react'; +import { useDispatch } from 'react-redux'; + +import { GUI } from 'dat.gui'; +import SceneNode from '../../SceneNode'; +import { subscribe_to_changes } from '../../subscriber'; +import { WebSocketContext } from '../WebSocket/WebSocket'; +import { drawCamera, drawSceneBounds } from './drawing'; + +const msgpack = require('msgpack-lite'); + +// manages setting up the scene and other logic for keeping state in sync with the server +export default function SetupScene() { + let scene = null; + let gui = null; + let sceneTree = null; + + // the websocket context + const socket = useContext(WebSocketContext).socket; + const dispatch = useDispatch(); + + // Scene + scene = new THREE.Scene(); + + // GUI + gui = new GUI(); + gui.domElement.id = 'datgui'; + const sceneFolder = gui.addFolder('Scene'); + sceneFolder.open(); + sceneTree = new SceneNode(scene, sceneFolder); + + // add objects to the the scene tree + const setObject = (path, object) => { + sceneTree.find(path.concat([''])).set_object(object); + }; + const deleteObject = (path) => { + sceneTree.delete(path); + }; + + // Axes + const axes = new THREE.AxesHelper(5); + setObject(['Axes'], axes); + + // Grid + const grid = new THREE.GridHelper(20, 20); + grid.rotateX(Math.PI / 2); // rotated to xy plane + setObject(['Grid'], grid); + + // Lights + const color = 0xffffff; + const intensity = 1; + const light = new THREE.AmbientLight(color, intensity); + setObject(['Light'], light); + + // draw scene bounds + const selector_fn_scene_bounds = (state) => { + return state.sceneState.sceneBounds; + }; + const fn_value_scene_bounds = (previous, current) => { + if (current !== null) { + const line = drawSceneBounds(current); + setObject(['Scene Bounds'], line); + } else { + deleteObject(['Scene Bounds']); + } + }; + subscribe_to_changes(selector_fn_scene_bounds, fn_value_scene_bounds); + + // draw camera + // NOTE: this has some issues right now! it won't + // update the camera on an individual change w/o deleting first + const selector_fn_cameras = (state) => { + return state.sceneState.cameras; + }; + const fn_value_cameras = (previous, current) => { + if (current !== null) { + let prev = new Set(); + if (previous !== null) { + prev = new Set(Object.keys(previous)); + } + const curr = new Set(Object.keys(current)); + // valid if in current but not previous + // invalid if in previous but not current + for (const key of curr) { + // valid so draw + if (!prev.has(key)) { + // keys_valid.push(key); + const json = current[key]; + const camera = drawCamera(json); + setObject(['Cameras', key], camera); + } + } + for (const key of prev) { + // invalid so delete + if (!curr.has(key) || current[key] === null) { + // keys_invalid.push(key); + deleteObject(['Cameras', key]); + } + } + } else { + deleteObject(['Cameras']); + } + }; + subscribe_to_changes(selector_fn_cameras, fn_value_cameras); + + useEffect(() => { + socket.addEventListener('message', (originalCmd) => { + // set the remote description when the offer is received + const cmd = msgpack.decode(new Uint8Array(originalCmd.data)); + if (cmd.type === 'write') { + // write to the store + dispatch({ + type: 'write', + path: cmd.path, + data: cmd.data, + }); + } + }); + }, []); // empty dependency array means only run once + + return scene; +} diff --git a/nerfactory/viewer/app/src/modules/Scene/drawing.js b/nerfactory/viewer/app/src/modules/Scene/drawing.js new file mode 100644 index 000000000..c91ade755 --- /dev/null +++ b/nerfactory/viewer/app/src/modules/Scene/drawing.js @@ -0,0 +1,164 @@ +/* eslint-disable no-restricted-syntax */ +// ---- code for drawing with three.js ---- +import * as THREE from 'three'; + +export function drawSceneBounds(sceneBounds): THREE.Object3D { + console.assert( + sceneBounds.type === 'aabb', + 'The scene bounds must be an AABB', + ); + + const box = sceneBounds; + + const w = 1.0; + const aaa = new THREE.Vector3(w, w, w); + const aab = new THREE.Vector3(w, w, -w); + const aba = new THREE.Vector3(w, -w, w); + const baa = new THREE.Vector3(-w, w, w); + const abb = new THREE.Vector3(w, -w, -w); + const bba = new THREE.Vector3(-w, -w, w); + const bab = new THREE.Vector3(-w, w, -w); + const bbb = new THREE.Vector3(-w, -w, -w); + let points = [aaa, aab, aaa, aba, aab, abb, aba, abb]; + points = points.concat([baa, bab, baa, bba, bab, bbb, bba, bbb]); + points = points.concat([aaa, baa, aab, bab, aba, bba, abb, bbb]); + + const maxPoint = new THREE.Vector3(...box.max_point); + const minPoint = new THREE.Vector3(...box.min_point); + + const lengths = maxPoint.clone(); + lengths.sub(minPoint); + + const scalar = lengths.clone(); + scalar.divide(new THREE.Vector3(2.0, 2.0, 2.0)); + + const offset = minPoint.clone(); + offset.add(scalar); + for (let i = 0; i < points.length; i += 1) { + points[i] = points[i].clone(); + points[i].multiply(scalar).add(offset); + } + + const geometry = new THREE.BufferGeometry().setFromPoints(points); + const material = new THREE.LineBasicMaterial({ + color: 0x000000, + linewidth: 1, + }); + const lines = new THREE.LineSegments(geometry, material); + return lines; +} + +export function getCameraWireframe( + scale = 0.3, + focalLength = 4, + w = 1.5, + h = 2, +) { + // Returns a wireframe of a 3D line-plot of a camera symbol. + // A wireframe is a frustum. + // https://github.com/hangg7/mvs_visual/blob/275d382a824733a3187a8e3147be184dd6f14795/mvs_visual.py#L54. + // scale: scale of rendering + // focalLength: this is the focal length + // w: width + // h: height + const f = focalLength; + + const ul = new THREE.Vector3(-w, h, -f); + const ur = new THREE.Vector3(w, h, -f); + const ll = new THREE.Vector3(-w, -h, -f); + const lr = new THREE.Vector3(w, -h, -f); + const C = new THREE.Vector3(0, 0, 0); + const points = [ + C, + ul, + C, + ur, + C, + ll, + C, + lr, + C, + ul, + ur, + ul, + lr, + ur, + lr, + ll, + ll, + ul, + ]; + + const scalar = new THREE.Vector3(scale, scale, scale); + for (let i = 0; i < points.length; i += 1) { + points[i].multiply(scalar); + } + + const geometry = new THREE.BufferGeometry().setFromPoints(points); + const material = new THREE.LineBasicMaterial({ + color: 0x000000, + linewidth: 1, + }); + const lines = new THREE.LineSegments(geometry, material); + return lines; +} + +export function drawCameraImagePlane(width, height, imageString) { + // imageString is the texture as a base64 string + const geometry = new THREE.PlaneGeometry(width, height); + const material = new THREE.MeshBasicMaterial({ + side: THREE.DoubleSide, + }); + const texture = new THREE.TextureLoader().load(imageString); + material.map = texture; + const plane = new THREE.Mesh(geometry, material); + return plane; +} + +function transpose(matrix) { + return matrix[0].map((col, i) => matrix.map((row) => row[i])); +} + +export function drawCamera(camera): THREE.Object3D { + const group = new THREE.Group(); + + // console.log(camera); + console.assert( + camera.type === 'PinholeCamera', + 'The camera should be a PinholeCamera', + ); + + const height = 0.25; + const f = camera.fx; + const displayedFocalLength = height / (2.0 * (camera.cy / f)); + const width = 2.0 * (camera.cx / f) * displayedFocalLength; + const cameraWireframeObject = getCameraWireframe(1.0, displayedFocalLength, width, height); + cameraWireframeObject.translateZ(displayedFocalLength); // move the wireframe frustum back + group.add(cameraWireframeObject); + const cameraImagePlaneObject = drawCameraImagePlane( + width*2, + height*2, + camera.image, + ); + group.add(cameraImagePlaneObject); + + // make homogeneous coordinates and then + // transpose and flatten the matrix into an array + let c2w = JSON.parse(JSON.stringify(camera.camera_to_world)); + c2w.push([0, 0, 0, 1]); + c2w = transpose(c2w).flat(); + + const mat = new THREE.Matrix4(); + mat.fromArray(c2w); + mat.decompose(group.position, group.quaternion, group.scale); + + return group; +} + +export function drawCameras(cameras): Record { + const cameraObjects = {}; + for (const [key, camera] of Object.entries(cameras)) { + cameraObjects[key] = drawCamera(camera); + } + return cameraObjects; +} diff --git a/nerfactory/viewer/app/src/modules/ViewerWindow/ViewerWindow.css b/nerfactory/viewer/app/src/modules/ViewerWindow/ViewerWindow.css new file mode 100644 index 000000000..2390151aa --- /dev/null +++ b/nerfactory/viewer/app/src/modules/ViewerWindow/ViewerWindow.css @@ -0,0 +1,27 @@ +.WebRTCVideo { + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; +} + +.WebRTCVideo-video { + width: 100%; + height: 100%; + object-fit: fill; +} + +.canvas-container-main { + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; +} + +.canvas-container-main canvas { + position: relative; + height: 100%; + width: 100%; +} diff --git a/nerfactory/viewer/app/src/modules/ViewerWindow/ViewerWindow.jsx b/nerfactory/viewer/app/src/modules/ViewerWindow/ViewerWindow.jsx new file mode 100644 index 000000000..f5611b586 --- /dev/null +++ b/nerfactory/viewer/app/src/modules/ViewerWindow/ViewerWindow.jsx @@ -0,0 +1,116 @@ +import './ViewerWindow.css'; + +import * as THREE from 'three'; + +import React, { useContext, useEffect, useRef } from 'react'; +import { useSelector } from 'react-redux'; + +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; +import WebRtcWindow from '../WebRtcWindow/WebRtcWindow'; +import { WebSocketContext } from '../WebSocket/WebSocket'; + +const msgpack = require('msgpack-lite'); + +// manages a camera and the web rtc stream... +export default function ViewerWindow(props) { + // eslint-disable-next-line react/prop-types + const scene = props.scene; + let cameraControls = null; + const myRef = useRef(null); + const websocket = useContext(WebSocketContext).socket; + const field_of_view = useSelector( + (state) => state.renderingState.field_of_view, + ); + + const getViewportWidth = () => { + return window.innerWidth - (window.innerWidth % 2); + }; + + const getViewportHeight = () => { + return window.innerHeight; + }; + + let viewportWidth = getViewportWidth(); + let viewportHeight = getViewportHeight(); + + const camera = new THREE.PerspectiveCamera( + field_of_view, + viewportWidth / viewportHeight, + 0.01, + 100, + ); + camera.position.x = 5; + camera.position.y = -5; + camera.position.z = 5; + camera.up = new THREE.Vector3(0, 0, 1); + + const renderer = new THREE.WebGLRenderer({ + antialias: true, + alpha: true, + }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(viewportWidth, viewportHeight); + renderer.domElement.style.border = '1px solid black'; + + const handleResize = () => { + viewportWidth = getViewportWidth(); + viewportHeight = getViewportHeight(); + camera.aspect = viewportWidth / viewportHeight; + camera.updateProjectionMatrix(); + renderer.setSize(viewportWidth, viewportHeight); + }; + + const sendCamera = () => { + // update the camera information in the python server + if (websocket.readyState === WebSocket.OPEN) { + const cmd = 'write'; + const path = 'renderingState/camera'; + const data = { + type: cmd, + path, + data: camera.toJSON(), + }; + const message = msgpack.encode(data); + websocket.send(message); + } + }; + + const update = () => { + handleResize(); + camera.updateProjectionMatrix(); + cameraControls.update(); + requestAnimationFrame(update); + renderer.render(scene, camera); + sendCamera(); + }; + + // similar to componentDidMount + useEffect(() => { + myRef.current.append(renderer.domElement); + // add camera controls + cameraControls = new OrbitControls(camera, renderer.domElement); + cameraControls.rotateSpeed = 2.0; + cameraControls.zoomSpeed = 0.3; + cameraControls.panSpeed = 0.2; + cameraControls.target.set(0, 0, 0); // focus point of the controls + cameraControls.autoRotate = false; + cameraControls.enableDamping = true; + cameraControls.dampingFactor = 1.0; + cameraControls.update(); + update(); + }, []); + + // useEffect(() => { + // camera.fov = field_of_view; + // console.log(field_of_view); + // camera.updateProjectionMatrix(); + // }, [field_of_view]); + + return ( +
+ {/* the webrtc viewer needs to know the camera pose */} + +
+
+ ); +} diff --git a/nerfactory/viewer/app/src/modules/ViewerWindow/ViewerWindowSlice.js b/nerfactory/viewer/app/src/modules/ViewerWindow/ViewerWindowSlice.js new file mode 100644 index 000000000..e69de29bb diff --git a/nerfactory/viewer/app/src/modules/WebRtcWindow/WebRtcWindow.jsx b/nerfactory/viewer/app/src/modules/WebRtcWindow/WebRtcWindow.jsx new file mode 100644 index 000000000..78bab30b7 --- /dev/null +++ b/nerfactory/viewer/app/src/modules/WebRtcWindow/WebRtcWindow.jsx @@ -0,0 +1,147 @@ +import React, { createContext, useContext, useEffect, useRef } from 'react'; +import { useDispatch } from 'react-redux'; + +import { WebSocketContext } from '../WebSocket/WebSocket'; + +const WebRtcContext = createContext(null); +const msgpack = require('msgpack-lite'); + +export { WebRtcContext }; + +export default function WebRtcWindow() { + const websocket = useContext(WebSocketContext).socket; + const pcRef = useRef(null); + const localVideoRef = useRef(null); + + const dispatch = useDispatch(); + + const getRTCPeerConnection = () => { + const pc = new RTCPeerConnection({ + iceServers: [ + { + urls: 'stun:openrelay.metered.ca:80', + }, + { + urls: 'turn:openrelay.metered.ca:80', + username: 'openrelayproject', + credential: 'openrelayproject', + }, + { + urls: 'turn:openrelay.metered.ca:443', + username: 'openrelayproject', + credential: 'openrelayproject', + }, + { + urls: 'turn:openrelay.metered.ca:443?transport=tcp', + username: 'openrelayproject', + credential: 'openrelayproject', + }, + ], + }); + // connect video + pc.addEventListener('track', (evt) => { + dispatch({ + type: "write", + path: 'webrtcState/isConnected', + data: true, + }); + if (evt.track.kind === 'video') { + [localVideoRef.current.srcObject] = evt.streams; // uses array destructuring + } + }); + pc.addTransceiver('video', { direction: 'recvonly' }); + + // for updating the status of the peer connection + pc.oniceconnectionstatechange = () => { + if (pc.iceConnectionState === 'connected') { + dispatch({ + type: "write", + path: 'webrtcState/isConnected', + data: true, + }); + } else if (pc.iceConnectionState === 'disconnected') { + dispatch({ + type: "write", + path: 'webrtcState/isConnected', + data: false, + }); + } + }; + return pc; + }; + + const sendOffer = () => { + pcRef.current + .createOffer() + .then((offer) => { + return pcRef.current.setLocalDescription(offer); + }) + .then(() => { + // wait for ICE gathering to complete + return new Promise((resolve) => { + if (pcRef.current.iceGatheringState === 'complete') { + resolve(); + } else { + const checkState = () => { + if (pcRef.current.iceGatheringState === 'complete') { + pcRef.current.removeEventListener( + 'icegatheringstatechange', + checkState, + ); + resolve(); + } + }; + pcRef.current.addEventListener( + 'icegatheringstatechange', + checkState, + ); + } + }); + }) + .then(() => { + // send the offer + const offer = pcRef.current.localDescription; + const cmd = 'offer'; + const path = ''; + const data = { + type: cmd, + path, + data: { + sdp: offer.sdp, + type: offer.type, + }, + }; + const message = msgpack.encode(data); + websocket.send(message); + }); + }; + + useEffect(() => { + websocket.addEventListener('message', (originalCmd) => { + // set the remote description when the offer is received + const cmd = msgpack.decode(new Uint8Array(originalCmd.data)); + if (cmd.type === 'answer') { + const answer = cmd.data; + pcRef.current.setRemoteDescription(answer); + } + }); + + websocket.addEventListener('open', () => { + // connect webrtc when the websocket is connected + pcRef.current = getRTCPeerConnection(); + sendOffer(); + }); + }, []); // empty dependency array means only run once + + return ( +
+
+ ); +} diff --git a/nerfactory/viewer/app/src/modules/WebSocket/WebSocket.jsx b/nerfactory/viewer/app/src/modules/WebSocket/WebSocket.jsx new file mode 100644 index 000000000..ab3668eac --- /dev/null +++ b/nerfactory/viewer/app/src/modules/WebSocket/WebSocket.jsx @@ -0,0 +1,86 @@ +// Much of this code comes from or is inspired by: +// https://www.pluralsight.com/guides/using-web-sockets-in-your-reactredux-app + +import React, { createContext } from 'react'; + +import PropTypes from 'prop-types'; +import { useDispatch } from 'react-redux'; + +const WebSocketContext = createContext(null); + +export { WebSocketContext }; + +function getWebsocketEndpointFromUrl(url: string) { + const splitUrl = url.split('?'); + if (splitUrl.length !== 2) { + window.alert("There should be exactly one '?' in the url."); + return ''; + } + const endpoint = splitUrl.pop(); + if (endpoint === '') { + const message = + 'Please set the websocket endpoint. E.g., a correct URL may be: http://localhost:4000?localhost:8051'; + window.alert(message); + return ''; + } + return endpoint; +} + +export default function WebSocketContextFunction({ children }) { + + const dispatch = useDispatch(); + + let socket; + let ws; + + // should look like ws://localhost:8051 + const websocketEndpoint = getWebsocketEndpointFromUrl(window.location.href); + + const connect = () => { + socket = new WebSocket(`ws://${websocketEndpoint}/`); + socket.binaryType = 'arraybuffer'; + socket.onopen = () => { + dispatch({ + type: 'write', + path: 'websocketState/isConnected', + data: true, + }); + }; + + socket.onclose = () => { + // when closed, the websocket will try to reconnect every second + dispatch({ + type: 'write', + path: 'websocketState/isConnected', + data: false, + }); + setTimeout(() => { + connect(); + }, 1000); + }; + + socket.onerror = (err) => { + console.error( + 'Socket encountered error: ', + err.message, + 'Closing socket', + ); + socket.close(); + }; + }; + + if (!socket) { + connect(); + ws = { + socket, + }; + } + + return ( + {children} + ); +} + +WebSocketContextFunction.propTypes = { + children: PropTypes.node.isRequired, +}; diff --git a/nerfactory/viewer/app/src/reducer.js b/nerfactory/viewer/app/src/reducer.js new file mode 100644 index 000000000..6475e9488 --- /dev/null +++ b/nerfactory/viewer/app/src/reducer.js @@ -0,0 +1,59 @@ +import { split_path } from './utils'; + +const initialState = { + // the websocket connection state + websocketState: { + isConnected: false, + }, + // the webrtc connection state + webrtcState: { + isConnected: false, + }, + // the rendering state + renderingState: { + camera: null, // the camera object used to render the three.js scene + isTraining: true, + output_options: ['rgb'], // populated by the possible Graph outputs + output_choice: 'rgb', // the selected output + minResolution: 50, + maxResolution: 512, + field_of_view: 80 + }, + // the scene state + sceneState: { + sceneBounds: null, + cameras: null, + }, + // ------------------------------------------------------------------------ + // which of the state keys to synchronize with the bridge server + sync_keys: ['renderingState', 'sceneState'], +}; + +function setData(newState, state, path, data) { + if (path.length === 1) { + newState[path[0]] = data; // eslint-disable-line no-param-reassign + } else { + newState[path[0]] = { ...state[path[0]] }; // eslint-disable-line no-param-reassign + setData(newState[path[0]], state[path[0]], path.slice(1), data); + } +} + +// Use the initialState as a default value +// eslint-disable-next-line default-param-last +export default function rootReducer(state = initialState, action) { + // The reducer normally looks at the action type field to decide what happens + + switch (action.type) { + case 'write': { + const path = split_path(action.path); // convert string with "/"s to a list + const data = action.data; + const newState = { ...state }; + setData(newState, state, path, data); + return newState; + } + default: + // If this reducer doesn't recognize the action type, or doesn't + // care about this specific action, return the existing state unchanged + return state; + } +} diff --git a/nerfactory/viewer/app/src/serviceWorker.js b/nerfactory/viewer/app/src/serviceWorker.js deleted file mode 100644 index c7cd6663d..000000000 --- a/nerfactory/viewer/app/src/serviceWorker.js +++ /dev/null @@ -1,141 +0,0 @@ -// This optional code is used to register a service worker. -// register() is not called by default. - -// This lets the app load faster on subsequent visits in production, and gives -// it offline capabilities. However, it also means that developers (and users) -// will only see deployed updates on subsequent visits to a page, after all the -// existing tabs open on the page have been closed, since previously cached -// resources are updated in the background. - -// To learn more about the benefits of this model and instructions on how to -// opt-in, read https://bit.ly/CRA-PWA - -const isLocalhost = Boolean( - window.location.hostname === "localhost" || - // [::1] is the IPv6 localhost address. - window.location.hostname === "[::1]" || - // 127.0.0.0/8 are considered localhost for IPv4. - window.location.hostname.match( - /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ - ) -); - -export function register(config) { - if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) { - // The URL constructor is available in all browsers that support SW. - const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); - if (publicUrl.origin !== window.location.origin) { - // Our service worker won't work if PUBLIC_URL is on a different origin - // from what our page is served on. This might happen if a CDN is used to - // serve assets; see https://github.com/facebook/create-react-app/issues/2374 - return; - } - - window.addEventListener("load", () => { - const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; - - if (isLocalhost) { - // This is running on localhost. Let's check if a service worker still exists or not. - checkValidServiceWorker(swUrl, config); - - // Add some additional logging to localhost, pointing developers to the - // service worker/PWA documentation. - navigator.serviceWorker.ready.then(() => { - console.log( - "This web app is being served cache-first by a service " + - "worker. To learn more, visit https://bit.ly/CRA-PWA" - ); - }); - } else { - // Is not localhost. Just register service worker - registerValidSW(swUrl, config); - } - }); - } -} - -function registerValidSW(swUrl, config) { - navigator.serviceWorker - .register(swUrl) - .then((registration) => { - registration.onupdatefound = () => { - const installingWorker = registration.installing; - if (installingWorker == null) { - return; - } - installingWorker.onstatechange = () => { - if (installingWorker.state === "installed") { - if (navigator.serviceWorker.controller) { - // At this point, the updated precached content has been fetched, - // but the previous service worker will still serve the older - // content until all client tabs are closed. - console.log( - "New content is available and will be used when all " + - "tabs for this page are closed. See https://bit.ly/CRA-PWA." - ); - - // Execute callback - if (config && config.onUpdate) { - config.onUpdate(registration); - } - } else { - // At this point, everything has been precached. - // It's the perfect time to display a - // "Content is cached for offline use." message. - console.log("Content is cached for offline use."); - - // Execute callback - if (config && config.onSuccess) { - config.onSuccess(registration); - } - } - } - }; - }; - }) - .catch((error) => { - console.error("Error during service worker registration:", error); - }); -} - -function checkValidServiceWorker(swUrl, config) { - // Check if the service worker can be found. If it can't reload the page. - fetch(swUrl, { - headers: { "Service-Worker": "script" }, - }) - .then((response) => { - // Ensure service worker exists, and that we really are getting a JS file. - const contentType = response.headers.get("content-type"); - if ( - response.status === 404 || - (contentType != null && contentType.indexOf("javascript") === -1) - ) { - // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then((registration) => { - registration.unregister().then(() => { - window.location.reload(); - }); - }); - } else { - // Service worker found. Proceed as normal. - registerValidSW(swUrl, config); - } - }) - .catch(() => { - console.log( - "No internet connection found. App is running in offline mode." - ); - }); -} - -export function unregister() { - if ("serviceWorker" in navigator) { - navigator.serviceWorker.ready - .then((registration) => { - registration.unregister(); - }) - .catch((error) => { - console.error(error.message); - }); - } -} diff --git a/nerfactory/viewer/app/src/store.js b/nerfactory/viewer/app/src/store.js new file mode 100644 index 000000000..9021d23a1 --- /dev/null +++ b/nerfactory/viewer/app/src/store.js @@ -0,0 +1,4 @@ +import { configureStore } from '@reduxjs/toolkit'; +import rootReducer from './reducer'; + +export default configureStore({ reducer: rootReducer }); diff --git a/nerfactory/viewer/app/src/subscriber.js b/nerfactory/viewer/app/src/subscriber.js new file mode 100644 index 000000000..3bc35562e --- /dev/null +++ b/nerfactory/viewer/app/src/subscriber.js @@ -0,0 +1,19 @@ +import { useContext } from 'react'; +import { ReactReduxContext } from 'react-redux'; + +export function subscribe_to_changes(selector_fn, fn) { + // selector_fn: returns a value from the redux state + // fn_valid: function to run on a valid input + // fn_null: function to run on a null input + const { store } = useContext(ReactReduxContext); + + let current; + const handleChange = () => { + const previous = current; + current = selector_fn(store.getState()); + if (previous !== current) { + fn(previous, current); + } + }; + store.subscribe(handleChange); +} diff --git a/nerfactory/viewer/app/yarn.lock b/nerfactory/viewer/app/yarn.lock index 29a7a064b..6954d36b9 100644 --- a/nerfactory/viewer/app/yarn.lock +++ b/nerfactory/viewer/app/yarn.lock @@ -26,12 +26,12 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.18.8": +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8": version "7.18.8" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== -"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": +"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.18.5", "@babel/core@^7.7.2", "@babel/core@^7.8.0": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.9.tgz#805461f967c77ff46c74ca0460ccf4fe933ddd59" integrity sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g== @@ -85,7 +85,7 @@ "@babel/helper-explode-assignable-expression" "^7.18.6" "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.18.9": +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== @@ -116,15 +116,13 @@ "@babel/helper-annotate-as-pure" "^7.18.6" regexpu-core "^5.1.0" -"@babel/helper-define-polyfill-provider@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" - integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== +"@babel/helper-define-polyfill-provider@^0.3.1", "@babel/helper-define-polyfill-provider@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz#bd10d0aca18e8ce012755395b05a79f45eca5073" + integrity sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg== dependencies: - "@babel/helper-compilation-targets" "^7.13.0" - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/traverse" "^7.13.0" + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" debug "^4.1.1" lodash.debounce "^4.0.8" resolve "^1.14.2" @@ -164,7 +162,7 @@ dependencies: "@babel/types" "^7.18.9" -"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.18.6": +"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== @@ -192,7 +190,7 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f" integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w== @@ -779,7 +777,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-react-constant-elements@^7.12.1": +"@babel/plugin-transform-react-constant-elements@^7.12.1", "@babel/plugin-transform-react-constant-elements@^7.17.12": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.18.9.tgz#ff6aeedd38f57ba6b41dcf824fcc8bcedb3e783f" integrity sha512-IrTYh1I3YCEL1trjknnlLKTp5JggjzhKl/d3ibzPc97JhpFcDTr38Jdek/oX4cFbS6By0bXJcOkpRvJ5ZHK2wQ== @@ -906,7 +904,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.16.4": +"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.16.4", "@babel/preset-env@^7.18.2": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.9.tgz#9b3425140d724fbe590322017466580844c7eaff" integrity sha512-75pt/q95cMIHWssYtyfjVlvI+QEZQThQbKvR9xH+F/Agtw/s4Wfc2V9Bwd/P39VtixB7oWxGdH4GteTTwYJWMg== @@ -998,7 +996,7 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/preset-react@^7.12.5", "@babel/preset-react@^7.16.0": +"@babel/preset-react@^7.12.5", "@babel/preset-react@^7.16.0", "@babel/preset-react@^7.17.12": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.18.6.tgz#979f76d6277048dc19094c217b507f3ad517dd2d" integrity sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg== @@ -1010,7 +1008,7 @@ "@babel/plugin-transform-react-jsx-development" "^7.18.6" "@babel/plugin-transform-react-pure-annotations" "^7.18.6" -"@babel/preset-typescript@^7.16.0": +"@babel/preset-typescript@^7.16.0", "@babel/preset-typescript@^7.17.12": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz#ce64be3e63eddc44240c6358daefac17b3186399" integrity sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ== @@ -1027,7 +1025,7 @@ core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== @@ -1043,7 +1041,7 @@ "@babel/parser" "^7.18.6" "@babel/types" "^7.18.6" -"@babel/traverse@^7.13.0", "@babel/traverse@^7.18.9", "@babel/traverse@^7.7.2": +"@babel/traverse@^7.18.9", "@babel/traverse@^7.7.2": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.9.tgz#deeff3e8f1bad9786874cb2feda7a2d77a904f98" integrity sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg== @@ -1059,7 +1057,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": +"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.18.4", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f" integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg== @@ -1212,15 +1210,20 @@ dependencies: "@hapi/hoek" "^9.0.0" -"@humanwhocodes/config-array@^0.9.2": - version "0.9.5" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" - integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== +"@humanwhocodes/config-array@^0.10.4": + version "0.10.4" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.10.4.tgz#01e7366e57d2ad104feea63e72248f22015c520c" + integrity sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" minimatch "^3.0.4" +"@humanwhocodes/gitignore-to-minimatch@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz#316b0a63b91c10e53f242efb4ace5c3b34e8728d" + integrity sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA== + "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" @@ -1740,6 +1743,16 @@ dependencies: "@babel/runtime" "^7.13.10" +"@reduxjs/toolkit@^1.8.3": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.8.3.tgz#9c6a9c497bde43a67618d37a4175a00ae12efeb2" + integrity sha512-lU/LDIfORmjBbyDLaqFN2JB9YmAT1BElET9y0ZszwhSBa5Ef3t6o5CrHupw5J1iOXwd+o92QfQZ8OJpwXvsssg== + dependencies: + immer "^9.0.7" + redux "^4.1.2" + redux-thunk "^2.4.1" + reselect "^4.1.5" + "@rollup/plugin-babel@^5.2.0": version "5.3.1" resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283" @@ -1800,9 +1813,9 @@ integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== "@sinclair/typebox@^0.24.1": - version "0.24.20" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.20.tgz#11a657875de6008622d53f56e063a6347c51a6dd" - integrity sha512-kVaO5aEFZb33nPMTZBxiPEkY+slxiPtqC7QX8f9B3eGOMBvEfuMfxp9DSTTCsRJPumPKjrge4yagyssO4q6qzQ== + version "0.24.24" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.24.tgz#1c201951882b0954b0689b0047584776e0053c1f" + integrity sha512-YaQ015FMwcveHbmI7w7X19f979CUCut2sf1gryWVV5nkAFr598jUhSpByKaC2BSBtJLT9lrDBfEo0Y2IFNipMQ== "@sindresorhus/is@^0.14.0": version "0.14.0" @@ -1848,41 +1861,81 @@ resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz#81ef61947bb268eb9d50523446f9c638fb355906" integrity sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg== +"@svgr/babel-plugin-add-jsx-attribute@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.3.1.tgz#b9a5d84902be75a05ede92e70b338d28ab63fa74" + integrity sha512-jDBKArXYO1u0B1dmd2Nf8Oy6aTF5vLDfLoO9Oon/GLkqZ/NiggYWZA+a2HpUMH4ITwNqS3z43k8LWApB8S583w== + "@svgr/babel-plugin-remove-jsx-attribute@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz#6b2c770c95c874654fd5e1d5ef475b78a0a962ef" integrity sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg== +"@svgr/babel-plugin-remove-jsx-attribute@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.3.1.tgz#4877995452efc997b36777abe1fde9705ef78e8b" + integrity sha512-dQzyJ4prwjcFd929T43Z8vSYiTlTu8eafV40Z2gO7zy/SV5GT+ogxRJRBIKWomPBOiaVXFg3jY4S5hyEN3IBjQ== + "@svgr/babel-plugin-remove-jsx-empty-expression@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz#25621a8915ed7ad70da6cea3d0a6dbc2ea933efd" integrity sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA== +"@svgr/babel-plugin-remove-jsx-empty-expression@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.3.1.tgz#2d67a0e92904c9be149a5b22d3a3797ce4d7b514" + integrity sha512-HBOUc1XwSU67fU26V5Sfb8MQsT0HvUyxru7d0oBJ4rA2s4HW3PhyAPC7fV/mdsSGpAvOdd8Wpvkjsr0fWPUO7A== + "@svgr/babel-plugin-replace-jsx-attribute-value@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz#0b221fc57f9fcd10e91fe219e2cd0dd03145a897" integrity sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ== +"@svgr/babel-plugin-replace-jsx-attribute-value@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.3.1.tgz#306f5247139c53af70d1778f2719647c747998ee" + integrity sha512-C12e6aN4BXAolRrI601gPn5MDFCRHO7C4TM8Kks+rDtl8eEq+NN1sak0eAzJu363x3TmHXdZn7+Efd2nr9I5dA== + "@svgr/babel-plugin-svg-dynamic-title@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz#139b546dd0c3186b6e5db4fefc26cb0baea729d7" integrity sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg== +"@svgr/babel-plugin-svg-dynamic-title@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.3.1.tgz#6ce26d34cbc93eb81737ef528528907c292e7aa2" + integrity sha512-6NU55Mmh3M5u2CfCCt6TX29/pPneutrkJnnDCHbKZnjukZmmgUAZLtZ2g6ZoSPdarowaQmAiBRgAHqHmG0vuqA== + "@svgr/babel-plugin-svg-em-dimensions@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz#6543f69526632a133ce5cabab965deeaea2234a0" integrity sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw== +"@svgr/babel-plugin-svg-em-dimensions@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.3.1.tgz#5ade2a724b290873c30529d1d8cd23523856287a" + integrity sha512-HV1NGHYTTe1vCNKlBgq/gKuCSfaRlKcHIADn7P8w8U3Zvujdw1rmusutghJ1pZJV7pDt3Gt8ws+SVrqHnBO/Qw== + "@svgr/babel-plugin-transform-react-native-svg@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz#00bf9a7a73f1cad3948cdab1f8dfb774750f8c80" integrity sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q== +"@svgr/babel-plugin-transform-react-native-svg@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.3.1.tgz#d654f509d692c3a09dfb475757a44bd9f6ad7ddf" + integrity sha512-2wZhSHvTolFNeKDAN/ZmIeSz2O9JSw72XD+o2bNp2QAaWqa8KGpn5Yk5WHso6xqfSAiRzAE+GXlsrBO4UP9LLw== + "@svgr/babel-plugin-transform-svg-component@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz#583a5e2a193e214da2f3afeb0b9e8d3250126b4a" integrity sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ== +"@svgr/babel-plugin-transform-svg-component@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.3.1.tgz#21a285dbffdce9567c437ebf0d081bf9210807e6" + integrity sha512-cZ8Tr6ZAWNUFfDeCKn/pGi976iWSkS8ijmEYKosP+6ktdZ7lW9HVLHojyusPw3w0j8PI4VBeWAXAmi/2G7owxw== + "@svgr/babel-preset@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-5.5.0.tgz#8af54f3e0a8add7b1e2b0fcd5a882c55393df327" @@ -1897,6 +1950,20 @@ "@svgr/babel-plugin-transform-react-native-svg" "^5.4.0" "@svgr/babel-plugin-transform-svg-component" "^5.5.0" +"@svgr/babel-preset@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-6.3.1.tgz#8bd1ead79637d395e9362b01dd37cfd59702e152" + integrity sha512-tQtWtzuMMQ3opH7je+MpwfuRA1Hf3cKdSgTtAYwOBDfmhabP7rcTfBi3E7V3MuwJNy/Y02/7/RutvwS1W4Qv9g== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "^6.3.1" + "@svgr/babel-plugin-remove-jsx-attribute" "^6.3.1" + "@svgr/babel-plugin-remove-jsx-empty-expression" "^6.3.1" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^6.3.1" + "@svgr/babel-plugin-svg-dynamic-title" "^6.3.1" + "@svgr/babel-plugin-svg-em-dimensions" "^6.3.1" + "@svgr/babel-plugin-transform-react-native-svg" "^6.3.1" + "@svgr/babel-plugin-transform-svg-component" "^6.3.1" + "@svgr/core@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@svgr/core/-/core-5.5.0.tgz#82e826b8715d71083120fe8f2492ec7d7874a579" @@ -1906,6 +1973,15 @@ camelcase "^6.2.0" cosmiconfig "^7.0.0" +"@svgr/core@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-6.3.1.tgz#752adf49d8d5473b15d76ca741961de093f715bd" + integrity sha512-Sm3/7OdXbQreemf9aO25keerZSbnKMpGEfmH90EyYpj1e8wMD4TuwJIb3THDSgRMWk1kYJfSRulELBy4gVgZUA== + dependencies: + "@svgr/plugin-jsx" "^6.3.1" + camelcase "^6.2.0" + cosmiconfig "^7.0.1" + "@svgr/hast-util-to-babel-ast@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz#5ee52a9c2533f73e63f8f22b779f93cd432a5461" @@ -1913,6 +1989,14 @@ dependencies: "@babel/types" "^7.12.6" +"@svgr/hast-util-to-babel-ast@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.3.1.tgz#59614e24d2a4a28010e02089213b3448d905769d" + integrity sha512-NgyCbiTQIwe3wHe/VWOUjyxmpUmsrBjdoIxKpXt3Nqc3TN30BpJG22OxBvVzsAh9jqep0w0/h8Ywvdk3D9niNQ== + dependencies: + "@babel/types" "^7.18.4" + entities "^4.3.0" + "@svgr/plugin-jsx@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz#1aa8cd798a1db7173ac043466d7b52236b369000" @@ -1923,6 +2007,16 @@ "@svgr/hast-util-to-babel-ast" "^5.5.0" svg-parser "^2.0.2" +"@svgr/plugin-jsx@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-6.3.1.tgz#de7b2de824296b836d6b874d498377896e367f50" + integrity sha512-r9+0mYG3hD4nNtUgsTXWGYJomv/bNd7kC16zvsM70I/bGeoCi/3lhTmYqeN6ChWX317OtQCSZZbH4wq9WwoXbw== + dependencies: + "@babel/core" "^7.18.5" + "@svgr/babel-preset" "^6.3.1" + "@svgr/hast-util-to-babel-ast" "^6.3.1" + svg-parser "^2.0.4" + "@svgr/plugin-svgo@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz#02da55d85320549324e201c7b2e53bf431fcc246" @@ -1932,6 +2026,15 @@ deepmerge "^4.2.2" svgo "^1.2.2" +"@svgr/plugin-svgo@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-6.3.1.tgz#3c1ff2efaed10e5c5d35a6cae7bacaedc18b5d4a" + integrity sha512-yJIjTDKPYqzFVjmsbH5EdIwEsmKxjxdXSGJVLeUgwZOZPAkNQmD1v7LDbOdOKbR44FG8465Du+zWPdbYGnbMbw== + dependencies: + cosmiconfig "^7.0.1" + deepmerge "^4.2.2" + svgo "^2.8.0" + "@svgr/webpack@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-5.5.0.tgz#aae858ee579f5fa8ce6c3166ef56c6a1b381b640" @@ -1946,6 +2049,20 @@ "@svgr/plugin-svgo" "^5.5.0" loader-utils "^2.0.0" +"@svgr/webpack@^6.2.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-6.3.1.tgz#001d03236ebb03bf47c0a4b92d5423e05095ebe6" + integrity sha512-eODxwIUShLxSMaRjzJtrj9wg89D75JLczvWg9SaB5W+OtVTkiC1vdGd8+t+pf5fTlBOy4RRXAq7x1E3DUl3D0A== + dependencies: + "@babel/core" "^7.18.5" + "@babel/plugin-transform-react-constant-elements" "^7.17.12" + "@babel/preset-env" "^7.18.2" + "@babel/preset-react" "^7.17.12" + "@babel/preset-typescript" "^7.17.12" + "@svgr/core" "^6.3.1" + "@svgr/plugin-jsx" "^6.3.1" + "@svgr/plugin-svgo" "^6.3.1" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -2106,9 +2223,9 @@ integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18": - version "4.17.29" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.29.tgz#2a1795ea8e9e9c91b4a4bbe475034b20c1ec711c" - integrity sha512-uMd++6dMKS32EOuw1Uli3e3BPgdLIXmezcfHv7N4c1s3gkhikBplORPpMq3fuWkxncZN1reb16d5n8yhQ80x7Q== + version "4.17.30" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz#0f2f99617fa8f9696170c46152ccf7500b34ac04" + integrity sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ== dependencies: "@types/node" "*" "@types/qs" "*" @@ -2131,6 +2248,14 @@ dependencies: "@types/node" "*" +"@types/hoist-non-react-statics@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" + integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/html-minifier-terser@^6.0.0": version "6.1.0" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" @@ -2180,20 +2305,20 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/mime@^1": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" - integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== +"@types/mime@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.0.tgz#e9a9903894405c6a6551f1774df4e64d9804d69c" + integrity sha512-fccbsHKqFDXClBZTDLA43zl0+TbxyIwyzIzwwhvoJvhNjOErCdeX2xJbURimv2EbSVUGav001PaCJg4mZxMl4w== "@types/node@*": - version "18.0.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.6.tgz#0ba49ac517ad69abe7a1508bc9b3a5483df9d5d7" - integrity sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw== + version "18.6.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.3.tgz#4e4a95b6fe44014563ceb514b2598b3e623d1c98" + integrity sha512-6qKpDtoaYLM+5+AFChLhHermMQxc3TOEFIDzrZLPRGHPrLEwqFkkT5Kx3ju05g6X7uDPazz3jHbKPX0KzCjntg== "@types/node@^16.11.26": - version "16.11.45" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.45.tgz#155b13a33c665ef2b136f7f245fa525da419e810" - integrity sha512-3rKg/L5x0rofKuuUt5zlXzOnKyIHXmIu5R8A0TuNDMF2062/AOIDBciFIjToLEJ/9F9DzkHNot+BpNsMI1OLdQ== + version "16.11.47" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.47.tgz#efa9e3e0f72e7aa6a138055dace7437a83d9f91c" + integrity sha512-fpP+jk2zJ4VW66+wAMFoBJlx1bxmBKx4DUFf68UHgdGCOuyUTDlLWqsaNPJh7xhNDykyJ9eIzAygilP/4WoN8g== "@types/parse-json@^4.0.0": version "4.0.0" @@ -2201,9 +2326,9 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.1.5": - version "2.6.3" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.3.tgz#68ada76827b0010d0db071f739314fa429943d0a" - integrity sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg== + version "2.6.4" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.4.tgz#ad899dad022bab6b5a9f0a0fe67c2f7a4a8950ed" + integrity sha512-fOwvpvQYStpb/zHMx0Cauwywu9yLDmzWiiQBC7gJyq5tYLUXFZvDG7VK1B7WBxxjBJNKFOZ0zLoOQn8vmATbhw== "@types/prop-types@*": version "15.7.5" @@ -2266,11 +2391,11 @@ "@types/express" "*" "@types/serve-static@*", "@types/serve-static@^1.13.10": - version "1.13.10" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" - integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== + version "1.15.0" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" + integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg== dependencies: - "@types/mime" "^1" + "@types/mime" "*" "@types/node" "*" "@types/sockjs@^0.3.33": @@ -2297,6 +2422,11 @@ resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756" integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg== +"@types/use-sync-external-store@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" + integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== + "@types/ws@^8.5.1": version "8.5.3" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" @@ -2324,13 +2454,13 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.5.0": - version "5.30.7" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.7.tgz#1621dabc1ae4084310e19e9efc80dfdbb97e7493" - integrity sha512-l4L6Do+tfeM2OK0GJsU7TUcM/1oN/N25xHm3Jb4z3OiDU4Lj8dIuxX9LpVMS9riSXQs42D1ieX7b85/r16H9Fw== + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.31.0.tgz#cae1967b1e569e6171bbc6bec2afa4e0c8efccfe" + integrity sha512-VKW4JPHzG5yhYQrQ1AzXgVgX8ZAJEvCz0QI6mLRX4tf7rnFfh5D8SKm0Pq6w5PyNfAWJk6sv313+nEt3ohWMBQ== dependencies: - "@typescript-eslint/scope-manager" "5.30.7" - "@typescript-eslint/type-utils" "5.30.7" - "@typescript-eslint/utils" "5.30.7" + "@typescript-eslint/scope-manager" "5.31.0" + "@typescript-eslint/type-utils" "5.31.0" + "@typescript-eslint/utils" "5.31.0" debug "^4.3.4" functional-red-black-tree "^1.0.1" ignore "^5.2.0" @@ -2339,75 +2469,75 @@ tsutils "^3.21.0" "@typescript-eslint/experimental-utils@^5.0.0": - version "5.30.7" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.30.7.tgz#a815318385b7c81e57ddb5f640895cc08879035f" - integrity sha512-r218ZVL0zFBYzEq8/9K2ZhRgsmKUhm8xd3sWChgvTbmP98kHGuY83IUl64SS9fx9OSBM9vMLdzBfox4eDdm/ZQ== + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.31.0.tgz#f2b23ebeebd31358ce44cf5be7c3411127627b84" + integrity sha512-Yiar0ggNPyOsvrslJBdOo3jc3wjI6NnmWOQBA8WhR54YPbVqTNLuuHC6zxEt8FIgMozerxRlAncwznEjK+cJVA== dependencies: - "@typescript-eslint/utils" "5.30.7" + "@typescript-eslint/utils" "5.31.0" "@typescript-eslint/parser@^5.5.0": - version "5.30.7" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.30.7.tgz#99d09729392aec9e64b1de45cd63cb81a4ddd980" - integrity sha512-Rg5xwznHWWSy7v2o0cdho6n+xLhK2gntImp0rJroVVFkcYFYQ8C8UJTSuTw/3CnExBmPjycjmUJkxVmjXsld6A== + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.31.0.tgz#7f42d7dcc68a0a6d80a0f3d9a65063aee7bb8d2c" + integrity sha512-UStjQiZ9OFTFReTrN+iGrC6O/ko9LVDhreEK5S3edmXgR396JGq7CoX2TWIptqt/ESzU2iRKXAHfSF2WJFcWHw== dependencies: - "@typescript-eslint/scope-manager" "5.30.7" - "@typescript-eslint/types" "5.30.7" - "@typescript-eslint/typescript-estree" "5.30.7" + "@typescript-eslint/scope-manager" "5.31.0" + "@typescript-eslint/types" "5.31.0" + "@typescript-eslint/typescript-estree" "5.31.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.30.7": - version "5.30.7" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.30.7.tgz#8269a931ef1e5ae68b5eb80743cc515c4ffe3dd7" - integrity sha512-7BM1bwvdF1UUvt+b9smhqdc/eniOnCKxQT/kj3oXtj3LqnTWCAM0qHRHfyzCzhEfWX0zrW7KqXXeE4DlchZBKw== +"@typescript-eslint/scope-manager@5.31.0": + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.31.0.tgz#f47a794ba84d9b818ab7f8f44fff55a61016c606" + integrity sha512-8jfEzBYDBG88rcXFxajdVavGxb5/XKXyvWgvD8Qix3EEJLCFIdVloJw+r9ww0wbyNLOTYyBsR+4ALNGdlalLLg== dependencies: - "@typescript-eslint/types" "5.30.7" - "@typescript-eslint/visitor-keys" "5.30.7" + "@typescript-eslint/types" "5.31.0" + "@typescript-eslint/visitor-keys" "5.31.0" -"@typescript-eslint/type-utils@5.30.7": - version "5.30.7" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.30.7.tgz#5693dc3db6f313f302764282d614cfdbc8a9fcfd" - integrity sha512-nD5qAE2aJX/YLyKMvOU5jvJyku4QN5XBVsoTynFrjQZaDgDV6i7QHFiYCx10wvn7hFvfuqIRNBtsgaLe0DbWhw== +"@typescript-eslint/type-utils@5.31.0": + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.31.0.tgz#70a0b7201360b5adbddb0c36080495aa08f6f3d9" + integrity sha512-7ZYqFbvEvYXFn9ax02GsPcEOmuWNg+14HIf4q+oUuLnMbpJ6eHAivCg7tZMVwzrIuzX3QCeAOqKoyMZCv5xe+w== dependencies: - "@typescript-eslint/utils" "5.30.7" + "@typescript-eslint/utils" "5.31.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.30.7": - version "5.30.7" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.30.7.tgz#18331487cc92d0f1fb1a6f580c8ec832528079d0" - integrity sha512-ocVkETUs82+U+HowkovV6uxf1AnVRKCmDRNUBUUo46/5SQv1owC/EBFkiu4MOHeZqhKz2ktZ3kvJJ1uFqQ8QPg== +"@typescript-eslint/types@5.31.0": + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.31.0.tgz#7aa389122b64b18e473c1672fb3b8310e5f07a9a" + integrity sha512-/f/rMaEseux+I4wmR6mfpM2wvtNZb1p9hAV77hWfuKc3pmaANp5dLAZSiE3/8oXTYTt3uV9KW5yZKJsMievp6g== -"@typescript-eslint/typescript-estree@5.30.7": - version "5.30.7" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.7.tgz#05da9f1b281985bfedcf62349847f8d168eecc07" - integrity sha512-tNslqXI1ZdmXXrHER83TJ8OTYl4epUzJC0aj2i4DMDT4iU+UqLT3EJeGQvJ17BMbm31x5scSwo3hPM0nqQ1AEA== +"@typescript-eslint/typescript-estree@5.31.0": + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.31.0.tgz#eb92970c9d6e3946690d50c346fb9b1d745ee882" + integrity sha512-3S625TMcARX71wBc2qubHaoUwMEn+l9TCsaIzYI/ET31Xm2c9YQ+zhGgpydjorwQO9pLfR/6peTzS/0G3J/hDw== dependencies: - "@typescript-eslint/types" "5.30.7" - "@typescript-eslint/visitor-keys" "5.30.7" + "@typescript-eslint/types" "5.31.0" + "@typescript-eslint/visitor-keys" "5.31.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.30.7", "@typescript-eslint/utils@^5.13.0": - version "5.30.7" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.30.7.tgz#7135be070349e9f7caa262b0ca59dc96123351bb" - integrity sha512-Z3pHdbFw+ftZiGUnm1GZhkJgVqsDL5CYW2yj+TB2mfXDFOMqtbzQi2dNJIyPqPbx9mv2kUxS1gU+r2gKlKi1rQ== +"@typescript-eslint/utils@5.31.0", "@typescript-eslint/utils@^5.13.0": + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.31.0.tgz#e146fa00dca948bfe547d665b2138a2dc1b79acd" + integrity sha512-kcVPdQS6VIpVTQ7QnGNKMFtdJdvnStkqS5LeALr4rcwx11G6OWb2HB17NMPnlRHvaZP38hL9iK8DdE9Fne7NYg== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.30.7" - "@typescript-eslint/types" "5.30.7" - "@typescript-eslint/typescript-estree" "5.30.7" + "@typescript-eslint/scope-manager" "5.31.0" + "@typescript-eslint/types" "5.31.0" + "@typescript-eslint/typescript-estree" "5.31.0" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/visitor-keys@5.30.7": - version "5.30.7" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.7.tgz#c093abae75b4fd822bfbad9fc337f38a7a14909a" - integrity sha512-KrRXf8nnjvcpxDFOKej4xkD7657+PClJs5cJVSG7NNoCNnjEdc46juNAQt7AyuWctuCgs6mVRc1xGctEqrjxWw== +"@typescript-eslint/visitor-keys@5.31.0": + version "5.31.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.31.0.tgz#b0eca264df01ce85dceb76aebff3784629258f54" + integrity sha512-ZK0jVxSjS4gnPirpVjXHz7mgdOsZUHzNYSfTw2yPa3agfbt9YfqaBiBZFSSxeBWnpWkzCxTfUpnzA3Vily/CSg== dependencies: - "@typescript-eslint/types" "5.30.7" + "@typescript-eslint/types" "5.31.0" eslint-visitor-keys "^3.3.0" "@use-gesture/core@10.2.17": @@ -2610,7 +2740,7 @@ acorn@^7.0.0, acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1: +acorn@^8.2.4, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0: version "8.8.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== @@ -2851,12 +2981,12 @@ attr-accept@^2.2.2: integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg== autoprefixer@^10.4.7: - version "10.4.7" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.7.tgz#1db8d195f41a52ca5069b7593be167618edbbedf" - integrity sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA== + version "10.4.8" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.8.tgz#92c7a0199e1cfb2ad5d9427bd585a3d75895b9e5" + integrity sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw== dependencies: - browserslist "^4.20.3" - caniuse-lite "^1.0.30001335" + browserslist "^4.21.3" + caniuse-lite "^1.0.30001373" fraction.js "^4.2.0" normalize-range "^0.1.2" picocolors "^1.0.0" @@ -2946,20 +3076,20 @@ babel-plugin-named-asset-import@^0.3.8: integrity sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q== babel-plugin-polyfill-corejs2@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" - integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== + version "0.3.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz#e4c31d4c89b56f3cf85b92558954c66b54bd972d" + integrity sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q== dependencies: - "@babel/compat-data" "^7.13.11" - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.3.2" semver "^6.1.1" babel-plugin-polyfill-corejs3@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" - integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ== + version "0.5.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz#d7e09c9a899079d71a8b670c6181af56ec19c5c7" + integrity sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/helper-define-polyfill-provider" "^0.3.2" core-js-compat "^3.21.0" babel-plugin-polyfill-regenerator@^0.3.1: @@ -3122,15 +3252,15 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.18.1, browserslist@^4.20.2, browserslist@^4.20.3, browserslist@^4.21.0, browserslist@^4.21.2: - version "4.21.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.2.tgz#59a400757465535954946a400b841ed37e2b4ecf" - integrity sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA== +browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.18.1, browserslist@^4.20.2, browserslist@^4.20.3, browserslist@^4.21.0, browserslist@^4.21.3: + version "4.21.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a" + integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ== dependencies: - caniuse-lite "^1.0.30001366" - electron-to-chromium "^1.4.188" + caniuse-lite "^1.0.30001370" + electron-to-chromium "^1.4.202" node-releases "^2.0.6" - update-browserslist-db "^1.0.4" + update-browserslist-db "^1.0.5" bser@2.1.1: version "2.1.1" @@ -3230,10 +3360,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001335, caniuse-lite@^1.0.30001366: - version "1.0.30001368" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001368.tgz#c5c06381c6051cd863c45021475434e81936f713" - integrity sha512-wgfRYa9DenEomLG/SdWgQxpIyvdtH3NW8Vq+tB6AwR9e56iOIcu1im5F/wNdDf04XlKHXqIx4N8Jo0PemeBenQ== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001370, caniuse-lite@^1.0.30001373: + version "1.0.30001373" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001373.tgz#2dc3bc3bfcb5d5a929bec11300883040d7b4b4be" + integrity sha512-pJYArGHrPp3TUqQzFYRmP/lwJlj8RCbVe3Gd3eJQkAV8SAC6b19XS9BjMvRdvaS8RMkaTN8ZhoHP6S1y8zzwEQ== case-sensitive-paths-webpack-plugin@^2.4.0: version "2.4.0" @@ -3314,6 +3444,11 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== +classnames@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" + integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== + clean-css@^5.2.2: version "5.3.1" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.1.tgz#d0610b0b90d125196a2894d35366f734e5d7aa32" @@ -3485,7 +3620,7 @@ config-chain@^1.1.11: ini "^1.3.4" proto-list "~1.2.1" -confusing-browser-globals@^1.0.11: +confusing-browser-globals@^1.0.10, confusing-browser-globals@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== @@ -3525,22 +3660,22 @@ cookie@0.5.0: integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== core-js-compat@^3.21.0, core-js-compat@^3.22.1: - version "3.23.5" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.23.5.tgz#11edce2f1c4f69a96d30ce77c805ce118909cd5b" - integrity sha512-fHYozIFIxd+91IIbXJgWd/igXIc8Mf9is0fusswjnGIWVG96y2cwyUdlCkGOw6rMLHKAxg7xtCIVaHsyOUnJIg== + version "3.24.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.24.1.tgz#d1af84a17e18dfdd401ee39da9996f9a7ba887de" + integrity sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw== dependencies: - browserslist "^4.21.2" + browserslist "^4.21.3" semver "7.0.0" core-js-pure@^3.20.2, core-js-pure@^3.8.1: - version "3.23.5" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.23.5.tgz#23daaa9af9230e50f10b0fa4b8e6b87402be4c33" - integrity sha512-8t78LdpKSuCq4pJYCYk8hl7XEkAX+BP16yRIwL3AanTksxuEf7CM83vRyctmiEL8NDZ3jpUcv56fk9/zG3aIuw== + version "3.24.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.24.1.tgz#8839dde5da545521bf282feb7dc6d0b425f39fd3" + integrity sha512-r1nJk41QLLPyozHUUPmILCEMtMw24NG4oWK6RbsDdjzQgg9ZvrUsPBj1MnG0wXXp1DCDU6j+wUvEmBSrtRbLXg== core-js@^3.19.2: - version "3.23.5" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.23.5.tgz#1f82b0de5eece800827a2f59d597509c67650475" - integrity sha512-7Vh11tujtAZy82da4duVreQysIoO2EvVrur7y6IzZkH1IHPSekuDi8Vuw1+YKjkbfWLRD7Nc9ICQ/sIUDutcyg== + version "3.24.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.24.1.tgz#cf7724d41724154010a6576b7b57d94c5d66e64f" + integrity sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg== core-util-is@~1.0.0: version "1.0.3" @@ -3558,7 +3693,7 @@ cosmiconfig@^6.0.0: path-type "^4.0.0" yaml "^1.7.2" -cosmiconfig@^7.0.0: +cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== @@ -3815,9 +3950,9 @@ data-urls@^2.0.0: whatwg-url "^8.0.0" date-fns@^2.16.1: - version "2.28.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2" - integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw== + version "2.29.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.1.tgz#9667c2615525e552b5135a3116b95b1961456e60" + integrity sha512-dlLD5rKaKxpFdnjrs+5azHDFOPEu4ANy/LTh04A1DTzMM7qoajmKCBc8pkKRFT41CNzw+4gQh79X5C+Jq27HAw== debug@2.6.9, debug@^2.2.0, debug@^2.6.0, debug@^2.6.9: version "2.6.9" @@ -4122,15 +4257,15 @@ electron-is-dev@^2.0.0: resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-2.0.0.tgz#833487a069b8dad21425c67a19847d9064ab19bd" integrity sha512-3X99K852Yoqu9AcW50qz3ibYBWY79/pBhlMCab8ToEWS48R0T9tyxRiQhwylE7zQdXrMnx2JKqUJyMPmt5FBqA== -electron-to-chromium@^1.4.188: - version "1.4.197" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.197.tgz#172054244cca16320ebc19459bd03d8a9018ff73" - integrity sha512-7EZCIDDraA2NUaHewLaAh6T63cZzgBmgDx/iiaeZ/pjSs36bOFEJ3hLIrn1TKCFhV0PEZZKu6qFPrxa/LGAzLg== +electron-to-chromium@^1.4.202: + version "1.4.206" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.206.tgz#580ff85b54d7ec0c05f20b1e37ea0becdd7b0ee4" + integrity sha512-h+Fadt1gIaQ06JaIiyqPsBjJ08fV5Q7md+V8bUvQW/9OvXfL2LRICTz2EcnnCP7QzrFTS6/27MRV6Bl9Yn97zA== electron@^19.0.4: - version "19.0.9" - resolved "https://registry.yarnpkg.com/electron/-/electron-19.0.9.tgz#e95bc18e14aac0af1290fcc7045001ef5ad96a64" - integrity sha512-ooEwrv8Y7NSzdhKcl6kPCYecnzcg5nFWuS5ryG+VFH3MMBR8zXh9nW2wLsZrBz6OGUxXrcc5BKBC7dA8C6RhGQ== + version "19.0.10" + resolved "https://registry.yarnpkg.com/electron/-/electron-19.0.10.tgz#4d2f03f307fbb70a295ff419112130b75661eda9" + integrity sha512-EiWtPWdD7CzkRkp1cw7t0N9W2qhI5XZOudHX7daOh5wI076nsdV2dtlAf/XyTHhPNoKR5qhTWrSnYL9PY6D1vg== dependencies: "@electron/get" "^1.14.1" "@types/node" "^16.11.26" @@ -4189,7 +4324,7 @@ engine.io-parser@~5.0.3: resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0" integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg== -enhanced-resolve@^5.9.3: +enhanced-resolve@^5.10.0: version "5.10.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz#0dc579c3bb2a1032e357ac45b8f3a6f3ad4fb1e6" integrity sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ== @@ -4202,6 +4337,11 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== +entities@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.1.tgz#c34062a94c865c322f9d67b4384e4169bcede6a4" + integrity sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg== + env-paths@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" @@ -4344,6 +4484,30 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" +eslint-config-airbnb-base@^15.0.0: + version "15.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz#6b09add90ac79c2f8d723a2580e07f3925afd236" + integrity sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig== + dependencies: + confusing-browser-globals "^1.0.10" + object.assign "^4.1.2" + object.entries "^1.1.5" + semver "^6.3.0" + +eslint-config-airbnb@19.0.4: + version "19.0.4" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz#84d4c3490ad70a0ffa571138ebcdea6ab085fdc3" + integrity sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew== + dependencies: + eslint-config-airbnb-base "^15.0.0" + object.assign "^4.1.2" + object.entries "^1.1.5" + +eslint-config-prettier@^8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" + integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== + eslint-config-react-app@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz#73ba3929978001c5c86274c017ea57eb5fa644b4" @@ -4438,7 +4602,7 @@ eslint-plugin-react-hooks@^4.3.0: resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== -eslint-plugin-react@^7.27.1: +eslint-plugin-react@^7.27.1, eslint-plugin-react@^7.28.0: version "7.30.1" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.30.1.tgz#2be4ab23ce09b5949c6631413ba64b2810fd3e22" integrity sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg== @@ -4459,9 +4623,9 @@ eslint-plugin-react@^7.27.1: string.prototype.matchall "^4.0.7" eslint-plugin-testing-library@^5.0.1: - version "5.5.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.5.1.tgz#6fe602f9082a421b471bbae8aed692e26fe981b3" - integrity sha512-plLEkkbAKBjPxsLj7x4jNapcHAg2ernkQlKKrN2I8NrQwPISZHyCUNvg5Hv3EDqOQReToQb5bnqXYbkijJPE/g== + version "5.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.6.0.tgz#91e810ecb838f86decc9b5202876c87e42d73ea7" + integrity sha512-y63TRzPhGCMNsnUwMGJU1MFWc/3GvYw+nzobp9QiyNTTKsgAt5RKAOT1I34+XqVBpX1lC8bScoOjCkP7iRv0Mw== dependencies: "@typescript-eslint/utils" "^5.13.0" @@ -4509,13 +4673,14 @@ eslint-webpack-plugin@^3.1.1: normalize-path "^3.0.0" schema-utils "^4.0.0" -eslint@^8.3.0: - version "8.20.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.20.0.tgz#048ac56aa18529967da8354a478be4ec0a2bc81b" - integrity sha512-d4ixhz5SKCa1D6SCPrivP7yYVi7nyD6A4vs6HIAul9ujBzcEmZVM3/0NN/yu5nKhmO1wjp5xQ46iRfmDGlOviA== +eslint@^8.2.0, eslint@^8.3.0: + version "8.21.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.21.0.tgz#1940a68d7e0573cef6f50037addee295ff9be9ef" + integrity sha512-/XJ1+Qurf1T9G2M5IHrsjp+xrGT73RZf23xA1z5wB1ZzzEAWSZKvRwhWxTFp1rvkvCfwcvAUNAP31bhKTTGfDA== dependencies: "@eslint/eslintrc" "^1.3.0" - "@humanwhocodes/config-array" "^0.9.2" + "@humanwhocodes/config-array" "^0.10.4" + "@humanwhocodes/gitignore-to-minimatch" "^1.0.2" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -4525,14 +4690,17 @@ eslint@^8.3.0: eslint-scope "^7.1.1" eslint-utils "^3.0.0" eslint-visitor-keys "^3.3.0" - espree "^9.3.2" + espree "^9.3.3" esquery "^1.4.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" + find-up "^5.0.0" functional-red-black-tree "^1.0.1" glob-parent "^6.0.1" globals "^13.15.0" + globby "^11.1.0" + grapheme-splitter "^1.0.4" ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" @@ -4550,12 +4718,12 @@ eslint@^8.3.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^9.3.2: - version "9.3.2" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.2.tgz#f58f77bd334731182801ced3380a8cc859091596" - integrity sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA== +espree@^9.3.2, espree@^9.3.3: + version "9.3.3" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.3.tgz#2dd37c4162bb05f433ad3c1a52ddf8a49dc08e9d" + integrity sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng== dependencies: - acorn "^8.7.1" + acorn "^8.8.0" acorn-jsx "^5.3.2" eslint-visitor-keys "^3.3.0" @@ -5181,6 +5349,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + gzip-size@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" @@ -5244,6 +5417,13 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + hoopy@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" @@ -5407,10 +5587,10 @@ icss-utils@^5.0.0, icss-utils@^5.1.0: resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== -idb@^6.1.4: - version "6.1.5" - resolved "https://registry.yarnpkg.com/idb/-/idb-6.1.5.tgz#dbc53e7adf1ac7c59f9b2bf56e00b4ea4fce8c7b" - integrity sha512-IJtugpKkiVXQn5Y+LteyBCNk1N8xpGV3wWZk9EVtZWH8DYkjBn0bX1XnGP9RkyZF0sAcywa6unHqSWKe7q4LGw== +idb@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/idb/-/idb-7.0.2.tgz#7a067e20dd16539938e456814b7d714ba8db3892" + integrity sha512-jjKrT1EnyZewQ/gCBb/eyiYrhGzws2FeY92Yx8qT9S9GeQAmo4JFVIiWRIfKW/6Ob9A+UDAOW9j9jn58fy2HIg== identity-obj-proxy@^3.0.0: version "3.0.0" @@ -6496,7 +6676,7 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -lilconfig@^2.0.3, lilconfig@^2.0.5: +lilconfig@^2.0.3, lilconfig@^2.0.5, lilconfig@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg== @@ -7842,6 +8022,11 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA== +prettier@2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" + integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== + pretty-bytes@^5.3.0, pretty-bytes@^5.4.1: version "5.6.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" @@ -8059,7 +8244,7 @@ react-error-overlay@^6.0.11: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb" integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== -react-is@^16.13.1: +react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -8074,6 +8259,18 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== +react-redux@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.0.2.tgz#bc2a304bb21e79c6808e3e47c50fe1caf62f7aad" + integrity sha512-nBwiscMw3NoP59NFCXFf02f8xdo+vSHT/uZ1ldDwF7XaTpzm+Phk97VT4urYBl5TYAPNVaFm12UHAEyzkpNzRA== + dependencies: + "@babel/runtime" "^7.12.1" + "@types/hoist-non-react-statics" "^3.3.1" + "@types/use-sync-external-store" "^0.0.3" + hoist-non-react-statics "^3.3.2" + react-is "^18.0.0" + use-sync-external-store "^1.0.0" + react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" @@ -8192,6 +8389,18 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +redux-thunk@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714" + integrity sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q== + +redux@^4.1.2, redux@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.0.tgz#46f10d6e29b6666df758780437651eeb2b969f13" + integrity sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA== + dependencies: + "@babel/runtime" "^7.9.2" + regenerate-unicode-properties@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" @@ -8290,6 +8499,11 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== +reselect@^4.1.5: + version "4.1.6" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.6.tgz#19ca2d3d0b35373a74dc1c98692cdaffb6602656" + integrity sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -8388,9 +8602,9 @@ rollup-plugin-terser@^7.0.0: terser "^5.0.0" rollup@^2.43.1: - version "2.77.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.77.0.tgz#749eaa5ac09b6baa52acc076bc46613eddfd53f4" - integrity sha512-vL8xjY4yOQEw79DvyXLijhnhh+R/O9zpF/LEgkCebZFtb6ELeN9H3/2T0r8+mp+fFTBHZ5qGpOpW2ela2zRt3g== + version "2.77.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.77.2.tgz#6b6075c55f9cc2040a5912e6e062151e42e2c4e3" + integrity sha512-m/4YzYgLcpMQbxX3NmAqDvwLATZzxt8bIegO78FZLl+lAgKJBd1DRAOeEiZcKOIOPjxE6ewHWHNgGEalFXuz1g== optionalDependencies: fsevents "~2.3.2" @@ -8996,7 +9210,7 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -svg-parser@^2.0.2: +svg-parser@^2.0.2, svg-parser@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== @@ -9020,7 +9234,7 @@ svgo@^1.2.2: unquote "~1.1.1" util.promisify "~1.0.0" -svgo@^2.7.0: +svgo@^2.7.0, svgo@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== @@ -9039,9 +9253,9 @@ symbol-tree@^3.2.4: integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== tailwindcss@^3.0.2: - version "3.1.6" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.1.6.tgz#bcb719357776c39e6376a8d84e9834b2b19a49f1" - integrity sha512-7skAOY56erZAFQssT1xkpk+kWt2NrO45kORlxFPXUt3CiGsVPhH1smuH5XoDH6sGPXLyBv+zgCKA2HWBsgCytg== + version "3.1.7" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.1.7.tgz#ce99425f30a74e01457a2e6a724463b0df3159ac" + integrity sha512-r7mgumZ3k0InfVPpGWcX8X/Ut4xBfv+1O/+C73ar/m01LxGVzWvPxF/w6xIUPEztrCoz7axfx0SMdh8FH8ZvRQ== dependencies: arg "^5.0.2" chokidar "^3.5.3" @@ -9052,7 +9266,7 @@ tailwindcss@^3.0.2: fast-glob "^3.2.11" glob-parent "^6.0.2" is-glob "^4.0.3" - lilconfig "^2.0.5" + lilconfig "^2.0.6" normalize-path "^3.0.0" object-hash "^3.0.0" picocolors "^1.0.0" @@ -9306,9 +9520,9 @@ type@^1.0.1: integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== type@^2.5.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/type/-/type-2.6.0.tgz#3ca6099af5981d36ca86b78442973694278a219f" - integrity sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ== + version "2.6.1" + resolved "https://registry.yarnpkg.com/type/-/type-2.6.1.tgz#808f389ec777205cc3cd97c1c88ec1a913105aae" + integrity sha512-OvgH5rB0XM+iDZGQ1Eg/o7IZn0XYJFVrN/9FQ4OWIYILyJJgVP2s1hLTOFn6UOZoDUI/HctGa0PFlE2n2HW3NQ== typedarray-to-buffer@^3.1.5: version "3.1.5" @@ -9392,7 +9606,7 @@ upath@^1.2.0: resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== -update-browserslist-db@^1.0.4: +update-browserslist-db@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q== @@ -9414,6 +9628,11 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" +use-sync-external-store@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + utf-8-validate@^5.0.2: version "5.0.9" resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.9.tgz#ba16a822fbeedff1a58918f2a6a6b36387493ea3" @@ -9507,7 +9726,7 @@ walker@^1.0.7: dependencies: makeerror "1.0.12" -watchpack@^2.3.1: +watchpack@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== @@ -9613,20 +9832,20 @@ webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.64.4: - version "5.73.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.73.0.tgz#bbd17738f8a53ee5760ea2f59dce7f3431d35d38" - integrity sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA== + version "5.74.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.74.0.tgz#02a5dac19a17e0bb47093f2be67c695102a55980" + integrity sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^0.0.51" "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/wasm-edit" "1.11.1" "@webassemblyjs/wasm-parser" "1.11.1" - acorn "^8.4.1" + acorn "^8.7.1" acorn-import-assertions "^1.7.6" browserslist "^4.14.5" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.9.3" + enhanced-resolve "^5.10.0" es-module-lexer "^0.9.0" eslint-scope "5.1.1" events "^3.2.0" @@ -9639,7 +9858,7 @@ webpack@^5.64.4: schema-utils "^3.1.0" tapable "^2.1.1" terser-webpack-plugin "^5.1.3" - watchpack "^2.3.1" + watchpack "^2.4.0" webpack-sources "^3.2.3" websocket-driver@>=0.5.1, websocket-driver@^0.7.4: @@ -9733,25 +9952,25 @@ word-wrap@^1.2.3, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -workbox-background-sync@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-6.5.3.tgz#7c66c1836aeca6f3762dc48d17a1852a33b3168c" - integrity sha512-0DD/V05FAcek6tWv9XYj2w5T/plxhDSpclIcAGjA/b7t/6PdaRkQ7ZgtAX6Q/L7kV7wZ8uYRJUoH11VjNipMZw== +workbox-background-sync@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz#3141afba3cc8aa2ae14c24d0f6811374ba8ff6a9" + integrity sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g== dependencies: - idb "^6.1.4" - workbox-core "6.5.3" + idb "^7.0.1" + workbox-core "6.5.4" -workbox-broadcast-update@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-6.5.3.tgz#fc2ad79cf507e22950cda9baf1e9a0ccc43f31bc" - integrity sha512-4AwCIA5DiDrYhlN+Miv/fp5T3/whNmSL+KqhTwRBTZIL6pvTgE4lVuRzAt1JltmqyMcQ3SEfCdfxczuI4kwFQg== +workbox-broadcast-update@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-6.5.4.tgz#8441cff5417cd41f384ba7633ca960a7ffe40f66" + integrity sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw== dependencies: - workbox-core "6.5.3" + workbox-core "6.5.4" -workbox-build@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-6.5.3.tgz#38e3f286d63d2745bff4d1478bb3a6ab5c8b1170" - integrity sha512-8JNHHS7u13nhwIYCDea9MNXBNPHXCs5KDZPKI/ZNTr3f4sMGoD7hgFGecbyjX1gw4z6e9bMpMsOEJNyH5htA/w== +workbox-build@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-6.5.4.tgz#7d06d31eb28a878817e1c991c05c5b93409f0389" + integrity sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA== dependencies: "@apideck/better-ajv-errors" "^0.3.1" "@babel/core" "^7.11.1" @@ -9775,132 +9994,132 @@ workbox-build@6.5.3: strip-comments "^2.0.1" tempy "^0.6.0" upath "^1.2.0" - workbox-background-sync "6.5.3" - workbox-broadcast-update "6.5.3" - workbox-cacheable-response "6.5.3" - workbox-core "6.5.3" - workbox-expiration "6.5.3" - workbox-google-analytics "6.5.3" - workbox-navigation-preload "6.5.3" - workbox-precaching "6.5.3" - workbox-range-requests "6.5.3" - workbox-recipes "6.5.3" - workbox-routing "6.5.3" - workbox-strategies "6.5.3" - workbox-streams "6.5.3" - workbox-sw "6.5.3" - workbox-window "6.5.3" + workbox-background-sync "6.5.4" + workbox-broadcast-update "6.5.4" + workbox-cacheable-response "6.5.4" + workbox-core "6.5.4" + workbox-expiration "6.5.4" + workbox-google-analytics "6.5.4" + workbox-navigation-preload "6.5.4" + workbox-precaching "6.5.4" + workbox-range-requests "6.5.4" + workbox-recipes "6.5.4" + workbox-routing "6.5.4" + workbox-strategies "6.5.4" + workbox-streams "6.5.4" + workbox-sw "6.5.4" + workbox-window "6.5.4" -workbox-cacheable-response@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-6.5.3.tgz#b1f8c2bc599a7be8f7e3c262535629c558738e47" - integrity sha512-6JE/Zm05hNasHzzAGKDkqqgYtZZL2H06ic2GxuRLStA4S/rHUfm2mnLFFXuHAaGR1XuuYyVCEey1M6H3PdZ7SQ== +workbox-cacheable-response@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-6.5.4.tgz#a5c6ec0c6e2b6f037379198d4ef07d098f7cf137" + integrity sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug== dependencies: - workbox-core "6.5.3" + workbox-core "6.5.4" -workbox-core@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-6.5.3.tgz#bca038a9ef0d7a634a6db2a60f45313ed22ac249" - integrity sha512-Bb9ey5n/M9x+l3fBTlLpHt9ASTzgSGj6vxni7pY72ilB/Pb3XtN+cZ9yueboVhD5+9cNQrC9n/E1fSrqWsUz7Q== +workbox-core@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-6.5.4.tgz#df48bf44cd58bb1d1726c49b883fb1dffa24c9ba" + integrity sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q== -workbox-expiration@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-6.5.3.tgz#efc0811f371a2ede1052b9de1c4f072b71d50503" - integrity sha512-jzYopYR1zD04ZMdlbn/R2Ik6ixiXbi15c9iX5H8CTi6RPDz7uhvMLZPKEndZTpfgmUk8mdmT9Vx/AhbuCl5Sqw== +workbox-expiration@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-6.5.4.tgz#501056f81e87e1d296c76570bb483ce5e29b4539" + integrity sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ== dependencies: - idb "^6.1.4" - workbox-core "6.5.3" + idb "^7.0.1" + workbox-core "6.5.4" -workbox-google-analytics@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-6.5.3.tgz#cc8c3a61f449131660a4ed2f5362d9a3599b18fe" - integrity sha512-3GLCHotz5umoRSb4aNQeTbILETcrTVEozSfLhHSBaegHs1PnqCmN0zbIy2TjTpph2AGXiNwDrWGF0AN+UgDNTw== +workbox-google-analytics@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-6.5.4.tgz#c74327f80dfa4c1954cbba93cd7ea640fe7ece7d" + integrity sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg== dependencies: - workbox-background-sync "6.5.3" - workbox-core "6.5.3" - workbox-routing "6.5.3" - workbox-strategies "6.5.3" + workbox-background-sync "6.5.4" + workbox-core "6.5.4" + workbox-routing "6.5.4" + workbox-strategies "6.5.4" -workbox-navigation-preload@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-6.5.3.tgz#81b74f598b11aa07e2cf1c21af7a826a4f0f70b3" - integrity sha512-bK1gDFTc5iu6lH3UQ07QVo+0ovErhRNGvJJO/1ngknT0UQ702nmOUhoN9qE5mhuQSrnK+cqu7O7xeaJ+Rd9Tmg== +workbox-navigation-preload@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-6.5.4.tgz#ede56dd5f6fc9e860a7e45b2c1a8f87c1c793212" + integrity sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng== dependencies: - workbox-core "6.5.3" + workbox-core "6.5.4" -workbox-precaching@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-6.5.3.tgz#c870312b2ef901d790ab9e48da084e776c62af47" - integrity sha512-sjNfgNLSsRX5zcc63H/ar/hCf+T19fRtTqvWh795gdpghWb5xsfEkecXEvZ8biEi1QD7X/ljtHphdaPvXDygMQ== +workbox-precaching@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-6.5.4.tgz#740e3561df92c6726ab5f7471e6aac89582cab72" + integrity sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg== dependencies: - workbox-core "6.5.3" - workbox-routing "6.5.3" - workbox-strategies "6.5.3" + workbox-core "6.5.4" + workbox-routing "6.5.4" + workbox-strategies "6.5.4" -workbox-range-requests@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-6.5.3.tgz#e624ac82ff266a5e4f236d055797def07949d941" - integrity sha512-pGCP80Bpn/0Q0MQsfETSfmtXsQcu3M2QCJwSFuJ6cDp8s2XmbUXkzbuQhCUzKR86ZH2Vex/VUjb2UaZBGamijA== +workbox-range-requests@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-6.5.4.tgz#86b3d482e090433dab38d36ae031b2bb0bd74399" + integrity sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg== dependencies: - workbox-core "6.5.3" + workbox-core "6.5.4" -workbox-recipes@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-6.5.3.tgz#15beac9d8ae7a3a1c100218094a824b4dd3fd59a" - integrity sha512-IcgiKYmbGiDvvf3PMSEtmwqxwfQ5zwI7OZPio3GWu4PfehA8jI8JHI3KZj+PCfRiUPZhjQHJ3v1HbNs+SiSkig== +workbox-recipes@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-6.5.4.tgz#cca809ee63b98b158b2702dcfb741b5cc3e24acb" + integrity sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA== dependencies: - workbox-cacheable-response "6.5.3" - workbox-core "6.5.3" - workbox-expiration "6.5.3" - workbox-precaching "6.5.3" - workbox-routing "6.5.3" - workbox-strategies "6.5.3" + workbox-cacheable-response "6.5.4" + workbox-core "6.5.4" + workbox-expiration "6.5.4" + workbox-precaching "6.5.4" + workbox-routing "6.5.4" + workbox-strategies "6.5.4" -workbox-routing@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-6.5.3.tgz#a0a699d8cc90b5692bd3df24679acbbda3913777" - integrity sha512-DFjxcuRAJjjt4T34RbMm3MCn+xnd36UT/2RfPRfa8VWJGItGJIn7tG+GwVTdHmvE54i/QmVTJepyAGWtoLPTmg== +workbox-routing@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-6.5.4.tgz#6a7fbbd23f4ac801038d9a0298bc907ee26fe3da" + integrity sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg== dependencies: - workbox-core "6.5.3" + workbox-core "6.5.4" -workbox-strategies@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-6.5.3.tgz#4bea9a48fee16cf43766e0d8138296773c8a9783" - integrity sha512-MgmGRrDVXs7rtSCcetZgkSZyMpRGw8HqL2aguszOc3nUmzGZsT238z/NN9ZouCxSzDu3PQ3ZSKmovAacaIhu1w== +workbox-strategies@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-6.5.4.tgz#4edda035b3c010fc7f6152918370699334cd204d" + integrity sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw== dependencies: - workbox-core "6.5.3" + workbox-core "6.5.4" -workbox-streams@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-6.5.3.tgz#b6860290031caa7d0e46ad7142315c94359c780b" - integrity sha512-vN4Qi8o+b7zj1FDVNZ+PlmAcy1sBoV7SC956uhqYvZ9Sg1fViSbOpydULOssVJ4tOyKRifH/eoi6h99d+sJ33w== +workbox-streams@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-6.5.4.tgz#1cb3c168a6101df7b5269d0353c19e36668d7d69" + integrity sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg== dependencies: - workbox-core "6.5.3" - workbox-routing "6.5.3" + workbox-core "6.5.4" + workbox-routing "6.5.4" -workbox-sw@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-6.5.3.tgz#cd2f0c086f4496acd25774ed02c48504189bebdd" - integrity sha512-BQBzm092w+NqdIEF2yhl32dERt9j9MDGUTa2Eaa+o3YKL4Qqw55W9yQC6f44FdAHdAJrJvp0t+HVrfh8AiGj8A== +workbox-sw@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-6.5.4.tgz#d93e9c67924dd153a61367a4656ff4d2ae2ed736" + integrity sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA== workbox-webpack-plugin@^6.4.1: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-6.5.3.tgz#c37bb323be4952311565c07db51054fe59c87d73" - integrity sha512-Es8Xr02Gi6Kc3zaUwR691ZLy61hz3vhhs5GztcklQ7kl5k2qAusPh0s6LF3wEtlpfs9ZDErnmy5SErwoll7jBA== + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-6.5.4.tgz#baf2d3f4b8f435f3469887cf4fba2b7fac3d0fd7" + integrity sha512-LmWm/zoaahe0EGmMTrSLUi+BjyR3cdGEfU3fS6PN1zKFYbqAKuQ+Oy/27e4VSXsyIwAw8+QDfk1XHNGtZu9nQg== dependencies: fast-json-stable-stringify "^2.1.0" pretty-bytes "^5.4.1" upath "^1.2.0" webpack-sources "^1.4.3" - workbox-build "6.5.3" + workbox-build "6.5.4" -workbox-window@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-6.5.3.tgz#4ade70056cb73477ef1cd8fea7cfd0ecbd825c7f" - integrity sha512-GnJbx1kcKXDtoJBVZs/P7ddP0Yt52NNy4nocjBpYPiRhMqTpJCNrSL+fGHZ/i/oP6p/vhE8II0sA6AZGKGnssw== +workbox-window@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-6.5.4.tgz#d991bc0a94dff3c2dbb6b84558cff155ca878e91" + integrity sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug== dependencies: "@types/trusted-types" "^2.0.2" - workbox-core "6.5.3" + workbox-core "6.5.4" wrap-ansi@^7.0.0: version "7.0.0" diff --git a/nerfactory/viewer/server/README.md b/nerfactory/viewer/server/README.md new file mode 100644 index 000000000..3554ebee7 --- /dev/null +++ b/nerfactory/viewer/server/README.md @@ -0,0 +1,20 @@ +# Python Kernel and Client Viewer App communication + +> The purpose of this document is to explain how to communicate from Python with the Client Viewer app. We will eventually move this into the read the docs. + +- Python Kernel (nerfactory code) +- Bridge Server +- Client Viewer App + +We have two types of components that we want to keep state updated in both locations. + +- Widgets + - The widgets are used to keep track of the +- SceneNode + - The scene nodes are used to represent the three.js objects. The properties relevant to these objects are the following: `"object", "transform", "properties"`. + +# Checklist + +- [ ] Currently using request-reply (REQ, REP with zmq). I.e., Python Kernel -> Bridge Server <-> Client Viewer App. We want a way to update the Python Kernel when the Bridge Server is updated. This requries some form of binding with callbacks. When the Bridge Server is updated, we want to update the binded Python variable. We can take inspiration from [ipywidgets](https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Basics.html). + +- [ ] \ No newline at end of file diff --git a/nerfactory/viewer/server/cameras.py b/nerfactory/viewer/server/cameras.py deleted file mode 100644 index 026592def..000000000 --- a/nerfactory/viewer/server/cameras.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright 2022 The Plenoptix Team. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Logic to render Camera objects in the Viewer""" - -from typing import List, Optional, Tuple - -import cv2 -import numpy as np - -from . import geometry as g -from .geometry import LineBasicMaterial, LineSegments, PointsGeometry - - -class ImagePlane(g.Mesh): - """Returns an image rendered within a specified plane - - Args: - image: image to be displayed - height: height of image. Defaults to 1. - width: width of image. Defaults to 1. - """ - - def __init__(self, image: np.ndarray, height: int = 1, width: int = 1): - # TODO(ethan): decide how to deal with the height and width - self.image = image - geometry = g.PlaneGeometry([width, height]) - material = g.MeshBasicMaterial( - map=g.ImageTexture(image=g.PngImage(cv2.imencode(".png", self.image[:, :, ::-1])[1].tobytes())) - ) - super().__init__(geometry, material) - - -def get_camera_wireframe(scale: float = 0.3, f: int = 4, w: int = 1.5, h: int = 2) -> np.ndarray: - """Returns a wireframe of a 3D line-plot of a camera symbol. - At https://github.com/hangg7/mvs_visual/blob/275d382a824733a3187a8e3147be184dd6f14795/mvs_visual.py#L54. - - Args: - scale: scale of rendering - f: this is the focal length - w: width - h: height - - Returns: - np.ndarray: stack of points corresponding to wireframe of camera - """ - ul = np.array([-w, h, -f]) - ur = np.array([w, h, -f]) - ll = np.array([-w, -h, -f]) - lr = np.array([w, -h, -f]) - C = np.zeros(3) - camera_points = [C, ul, C, ur, C, ll, C, lr, C, ul, ur, ul, lr, ur, lr, ll, ll, ul] - lines = np.stack(camera_points) * scale - return lines - - -def get_plane_pts( - focal_length: Tuple[float, float] = (1.0, 1.0), - image_size: Tuple[int, int] = (10, 10), - camera_scale: int = 1, - scale_factor: float = 1 / 4, -) -> np.ndarray: - """Returns points on the image plane given camera intrinsics - - Args: - focal_length: focal length of camera. Defaults to (1.0, 1.0). - image_size: height and width of image. Defaults to (10, 10). - camera_scale: camera intrinsics scale. Defaults to 1. - scale_factor: image scale. Defaults to 1/4. - """ - Z = -(focal_length[0] + focal_length[1]) / 2 * camera_scale - X0, Y0, X1, Y1 = ( - -image_size[0] / 2 * camera_scale, - image_size[1] / 2 * camera_scale, - image_size[0] / 2 * camera_scale, - -image_size[1] / 2 * camera_scale, - ) - - # scale image to plane such that it can go outside of the x0x1 range. - W, H = X1 - X0, Y0 - Y1 - w, h = image_size - ratio = min(w / W, h / H) - oW, oH = w / ratio, h / ratio # pylint: disable=invalid-name - - X0, Y0, X1, Y1 = -oW / 2, oH / 2, oW / 2, -oH / 2 - wsteps, hsteps = int(w * scale_factor), int(h * scale_factor) - Ys, Xs = np.meshgrid( - np.linspace(Y0, Y1, num=hsteps), - np.linspace(X0, X1, num=wsteps), - indexing="ij", - ) - Zs = np.ones_like(Xs) * Z - plane_pts = np.stack([Xs, Ys, Zs], axis=-1) - return plane_pts - - -def frustum( - scale: float = 1.0, - color: Optional[List[float]] = None, - focal_length: int = 4, - width: float = 1.5, - height: int = 2, -) -> LineSegments: - """Draws the camera frustums, returning line segments representing the frustums - - Args: - scale: scale of the rendering. Defaults to 1.0. - color: color of lines. Defaults to [0, 0, 0]. - focal_length: focal length of camera. Defaults to 4. - width: width of wireframe. Defaults to 1.5. - height: height of wireframe. Defaults to 2. - """ - # color - color of lines using R, G, B. default is black - if color is None: - color = [0, 0, 0] - - # TODO(ethan): make the scale adjustable depending on the camera size - # print("linewidth") - camera_wireframe_lines = get_camera_wireframe(scale=scale, f=focal_length, w=width / 2.0, h=height / 2.0) - N = len(camera_wireframe_lines) - colors = np.array([color for _ in range(N)]) - line_segments = LineSegments( - PointsGeometry(position=camera_wireframe_lines.astype(np.float32).T, color=colors.astype(np.float32).T), - LineBasicMaterial(vertexColors=True), - ) - return line_segments diff --git a/nerfactory/viewer/server/commands.py b/nerfactory/viewer/server/commands.py deleted file mode 100644 index fb71748f9..000000000 --- a/nerfactory/viewer/server/commands.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright 2022 The Plenoptix Team. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Commands callable from WebSocket Server bridge -""" - - -from dataclasses import dataclass -from typing import Any, Dict, List - -import numpy as np - -from nerfactory.viewer.server.geometry import ( - Mesh, - MeshPhongMaterial, - Object, - OrthographicCamera, - PerspectiveCamera, - Points, - PointsMaterial, -) -from nerfactory.viewer.server.path import Path - - -class SetObject: # pylint: disable=too-few-public-methods - """Set object command class: Instantiates and sets the object according to object type - - Args: - geometry_or_object: object to be set - material: material to be set. Defaults to None. - path: path of object in tree. Defaults to None. - - Raises: - ValueError: error if material is not set for object - """ - - __slots__ = ["object", "path"] - - def __init__(self, geometry_or_object, material=None, path=None): - if isinstance(geometry_or_object, Object): - if material is not None: - raise ValueError("Please supply either an Object OR a Geometry and a Material") - self.object = geometry_or_object - elif isinstance(geometry_or_object, (OrthographicCamera, PerspectiveCamera)): - self.object = geometry_or_object - else: - if material is None: - material = MeshPhongMaterial() - if isinstance(material, PointsMaterial): - self.object = Points(geometry_or_object, material) - else: - self.object = Mesh(geometry_or_object, material) - if path is not None: - self.path = path - else: - self.path = Path() - - def lower(self) -> Dict[str, Any]: - """creates properly formatted json""" - return {"type": "set_object", "object": self.object.lower(), "path": self.path.lower()} - - -@dataclass -class GetObject: - """Get object command class - - Args: - path: path of object in tree. Defaults to None. - """ - - __slots__ = ["path"] - - path: str - - def lower(self) -> Dict[str, Any]: - """creates properly formatted json with object""" - return {"type": "get_object", "path": self.path.lower()} - - -@dataclass -class SetTransform: - """Set transform command class""" - - __slots__ = ["matrix", "path"] - - matrix: np.ndarray - path: str - - def lower(self) -> Dict[str, Any]: - """creates properly formatted json with transform matrix""" - return {"type": "set_transform", "path": self.path.lower(), "matrix": list(self.matrix.T.flatten())} - - -@dataclass -class SetOutputOptions: - """Set output options command class""" - - __slots__ = ["output_options", "path"] - - output_options: List[str] - path: str - - def lower(self) -> Dict[str, Any]: - """creates properly formatted json with list of possible output options""" - return {"type": "set_output_options", "path": self.path.lower(), "output_options": list(self.output_options)} - - -@dataclass -class Delete: - """Delete current state command class""" - - __slots__ = ["path"] - - path: str - - def lower(self) -> Dict[str, Any]: - """creates properly formatted json updating tree""" - return {"type": "delete", "path": self.path.lower()} - - -@dataclass -class SetProperty: - """Set property command class""" - - __slots__ = ["path", "key", "value"] - - key: str - value: list - path: str - - def lower(self) -> Dict[str, Any]: - """creates properly formatted json setting property""" - return {"type": "set_property", "path": self.path.lower(), "property": self.key.lower(), "value": self.value} diff --git a/nerfactory/viewer/server/geometry.py b/nerfactory/viewer/server/geometry.py deleted file mode 100644 index a0a822f74..000000000 --- a/nerfactory/viewer/server/geometry.py +++ /dev/null @@ -1,914 +0,0 @@ -# Copyright 2022 The Plenoptix Team. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Geometry to render meshes, textures, etc. -""" - - -import base64 -import sys -import uuid -from typing import Any, BinaryIO, Dict, Iterable, List, Optional, Tuple - -import numpy as np -import umsgpack - -UNICODE = str -from io import BytesIO, StringIO - - -class SceneElement: - """Base class for objects in the scene""" - - def __init__(self): - self.uuid = UNICODE(uuid.uuid1()) - - def intrinsic_transform(self): # pylint: disable=no-self-use - """function for intrinsic transformation""" - return np.identity(4) - - def lower(self, object_data: Dict[str, Any]) -> Dict[str, Any]: # pylint: disable=no-self-use - """Formatting contents of object dictionary, returns a dictionary of formatted content - - Args: - object_data: object being sent via websocket - """ - return object_data - - -class ReferenceSceneElement(SceneElement): - """Generic scene element class""" - - field = None - - def lower_in_object(self, object_data: Dict[str, Any]) -> str: - """formatting contents of object dictionary, returns a dictionary of formatted content - - Args: - object_data: object being sent via websocket - """ - object_data.setdefault(self.field, []).append(self.lower(object_data)) - return self.uuid - - -class Geometry(ReferenceSceneElement): - """Geometry class""" - - field = "geometries" - - -class Material(ReferenceSceneElement): - """Material class""" - - field = "materials" - - -class Texture(ReferenceSceneElement): - """Texture class""" - - field = "textures" - - -class Image(ReferenceSceneElement): - """Image class""" - - field = "images" - - -class Box(Geometry): - """Box object - - Args: - lengths: length of side of box - """ - - def __init__(self, lengths: List[float]): - super().__init__() - self.lengths = lengths - - def lower(self, object_data): - return { - "uuid": self.uuid, - "type": "BoxGeometry", - "width": self.lengths[0], - "height": self.lengths[1], - "depth": self.lengths[2], - } - - -class Sphere(Geometry): - """Sphere object - - Args: - radius: radius of sphere - """ - - def __init__(self, radius: float): - super().__init__() - self.radius = radius - - def lower(self, object_data): - return { - "uuid": self.uuid, - "type": "SphereGeometry", - "radius": self.radius, - "widthSegments": 20, - "heightSegments": 20, - } - - -class Ellipsoid(Sphere): - """ - An Ellipsoid is treated as a Sphere of unit radius, with an affine - transformation applied to distort it into the ellipsoidal shape - - Args: - radii: the radii of epllipsoid - """ - - def __init__(self, radii: List[float]): - super().__init__(1.0) - self.radii = radii - - def intrinsic_transform(self): - return np.diag(np.hstack((self.radii, 1.0))) - - -class PlaneGeometry(Geometry): - """Plane Class - - Args: - lengths: the height and width of plane - """ - - def __init__(self, lengths: List[float]): - super().__init__() - self.lengths = lengths - - def lower(self, object_data): - return {"uuid": self.uuid, "type": "PlaneGeometry", "width": self.lengths[0], "height": self.lengths[1]} - - -class Cylinder(Geometry): - """A cylinder of the given height and radius. By Three.js convention, the axis of - rotational symmetry is aligned with the y-axis. - - Args: - height: height of cylinder - radius: radius for both top and bottom if top/bottom not given. Defaults to 1.0. - radius_top: radius of top. Defaults to None. - radius_bottom: radius of bottom. Defaults to None. - """ - - def __init__( - self, - height: int, - radius: float = 1.0, - radius_top: Optional[float] = None, - radius_bottom: Optional[float] = None, - ): - super().__init__() - if radius_top is not None and radius_bottom is not None: - self.radius_top = radius_top - self.radius_bottom = radius_bottom - else: - self.radius_top = radius - self.radius_bottom = radius - self.height = height - self.radial_segments = 50 - - def lower(self, object_data): - return { - "uuid": self.uuid, - "type": "CylinderGeometry", - "radiusTop": self.radius_top, - "radiusBottom": self.radius_bottom, - "height": self.height, - "radialSegments": self.radial_segments, - } - - -class GenericMaterial(Material): - """Generic Material base class - - Args: - color: color of material. Defaults to 0xFFFFFF. - reflectivity: reflectivity index. Defaults to 0.5. - map: texture map of object. Defaults to None. - transparent: transparency value. Defaults to None. - opacity: opacity value. Defaults to 1.0. - linewidth: width of lines. Defaults to 1.0. - wireframe: whether to render wireframe around object. Defaults to False. - wireframe_linewidth: width of wireframe lines. Defaults to 1.0. - vertex_colors: whether to render vertex colors. Defaults to False. - """ - - def __init__( - self, - color: int = 0xFFFFFF, - reflectivity: float = 0.5, - map: Optional[Texture] = None, # pylint: disable=redefined-builtin - side: int = 2, - transparent: Optional[bool] = None, - opacity: float = 1.0, - linewidth: float = 1.0, - wireframe: bool = False, - wireframe_linewidth: float = 1.0, - vertex_colors: bool = False, - **kwargs, - ): - super().__init__() - self.color = color - self.reflectivity = reflectivity - self.map = map - self.side = side - self.transparent = transparent - self.opacity = opacity - self.linewidth = linewidth - self.wireframe = wireframe - self.wireframe_linewidth = wireframe_linewidth - self.vertex_colors = vertex_colors - self.properties = kwargs - self._type = self._type - - def lower(self, object_data): - # Three.js allows a material to have an opacity which is != 1, - # but to still be non-transparent, in which case the opacity only - # serves to desaturate the material's color. That's a pretty odd - # combination of things to want, so by default we just use the - # opacity value to decide whether to set transparent to True or - # False. - if self.transparent is None: - transparent = bool(self.opacity != 1) - else: - transparent = self.transparent - data = { - "uuid": self.uuid, - "type": self._type, - "color": self.color, - "reflectivity": self.reflectivity, - "side": self.side, - "transparent": transparent, - "opacity": self.opacity, - "linewidth": self.linewidth, - "wireframe": bool(self.wireframe), - "wireframeLinewidth": self.wireframe_linewidth, - "vertexColors": (2 if self.vertex_colors else 0), # three.js wants an enum - } - data.update(self.properties) - if self.map is not None: - data["map"] = self.map.lower_in_object(object_data) - return data - - -class MeshBasicMaterial(GenericMaterial): - """Basic material mesh class""" - - _type = "MeshBasicMaterial" - - -class MeshPhongMaterial(GenericMaterial): - """Phong material mesh class""" - - _type = "MeshPhongMaterial" - - -class MeshLambertMaterial(GenericMaterial): - """Lambert material mesh class""" - - _type = "MeshLambertMaterial" - - -class MeshToonMaterial(GenericMaterial): - """Toon material mesh class""" - - _type = "MeshToonMaterial" - - -class LineBasicMaterial(GenericMaterial): - """Basic Line Material class""" - - _type = "LineBasicMaterial" - - -class PngImage(Image): - """Png Image""" - - def __init__(self, data): - super().__init__() - self.data = data - - @staticmethod - def from_file(fname: str): - """Read in png image from file - - Args: - fname: file name to read image from - """ - with open(fname, "rb") as f: - return PngImage(f.read()) - - def lower(self, object_data): - return { - "uuid": self.uuid, - "url": UNICODE("data:image/png;base64," + base64.b64encode(self.data).decode("ascii")), - } - - -class GenericTexture(Texture): - """Generic texture class""" - - def __init__(self, properties: Iterable): - super().__init__() - self.properties = properties - - def lower(self, object_data): - data = {"uuid": self.uuid} - data.update(self.properties) - if "image" in data: - image = data["image"] - data["image"] = image.lower_in_object(object_data) - return data - - -class ImageTexture(Texture): - """Image Texture class - - Args: - image: image represented as byte array - wrap: wrap format. Defaults to None. - repeat: repeat format. Defaults to None. - """ - - def __init__(self, image: bytearray, wrap: List[int] = None, repeat: List[int] = None, **kwargs): - super().__init__() - if wrap is None: - wrap = [1001, 1001] - if repeat is None: - repeat = [1, 1] - self.image = image - self.wrap = wrap - self.repeat = repeat - self.properties = kwargs - - def lower(self, object_data): - data = { - "uuid": self.uuid, - "wrap": self.wrap, - "repeat": self.repeat, - "image": self.image.lower_in_object(object_data), - } - data.update(self.properties) - return data - - -class Object(SceneElement): - """Object element within scene - - Args: - geometry: geometry of the object - material: material composition of object. Defaults to MeshPhongMaterial(). - """ - - def __init__(self, geometry: Geometry, material: GenericMaterial = MeshPhongMaterial()): - super().__init__() - self.geometry = geometry - self.material = material - self._type = self._type - - # pylint: arguments-differ - def lower(self): - data = { - "metadata": { - "version": 4.5, - "type": "Object", - }, - "geometries": [], - "materials": [], - "object": { - "uuid": self.uuid, - "type": self._type, - "geometry": self.geometry.uuid, - "material": self.material.uuid, - "matrix": list(self.geometry.intrinsic_transform().flatten()), - }, - } - self.geometry.lower_in_object(data) - self.material.lower_in_object(data) - return data - - -class Mesh(Object): - """Mesh class""" - - _type = "Mesh" - - -class OrthographicCamera(SceneElement): - """Orthographic camera class - Args: - left: left bounds - right: right bounds - top: top bounds - bottom: bottom bounds - near: near plane - far: far plane - zoom: zoom param. Defaults to 1. - """ - - def __init__(self, left: float, right: float, top: float, bottom: float, near: float, far: float, zoom: int = 1): - super().__init__() - self.left = left - self.right = right - self.top = top - self.bottom = bottom - self.near = near - self.far = far - self.zoom = zoom - - def lower(self, object_data): - data = { - "object": { - "uuid": self.uuid, - "type": "OrthographicCamera", - "left": self.left, - "right": self.right, - "top": self.top, - "bottom": self.bottom, - "near": self.near, - "far": self.far, - "zoom": self.zoom, - } - } - return data - - -class PerspectiveCamera(SceneElement): - """ - The PerspectiveCamera is the default camera used by the nerfactory viewer. See - https://threejs.org/docs/#api/en/cameras/PerspectiveCamera for more - information. - - Args: - fov : Camera frustum vertical field of view, from bottom to top of view, in degrees. Default is 50. - aspect: Camera frustum aspect ratio, usually the canvas width / canvas height. Default is 1 (square canvas). - near : Camera frustum near plane. Default is 0.1. The valid range is greater than 0 and less than the current - value of the far plane. Note that, unlike for the OrthographicCamera, 0 is not a valid value for a - PerspectiveCamera's near plane. - far : Camera frustum far plane. Default is 2000. - zoom : Gets or sets the zoom factor of the camera. Default is 1. - film_gauge: Film size used for the larger axis. Default is 35 (millimeters). This parameter does not influence - the projection matrix unless .filmOffset is set to a nonzero value. - film_offset: Horizontal off-center offset in the same unit as .filmGauge. Default is 0. - focus: Object distance used for stereoscopy and depth-of-field effects. This parameter does not influence - the projection matrix unless a StereoCamera is being used. Default is 10. - """ - - def __init__( - self, - fov: float = 50, - aspect: float = 1, - near: float = 0.1, - far: float = 2000, - zoom: int = 1, - film_gauge: float = 35, - film_offset: float = 0, - focus: float = 10, - ): - super().__init__() - self.fov = fov - self.aspect = aspect - self.far = far - self.near = near - self.zoom = zoom - self.film_gauge = film_gauge - self.film_offset = film_offset - self.focus = focus - - def lower(self, object_data): - data = { - "object": { - "uuid": self.uuid, - "type": "PerspectiveCamera", - "aspect": self.aspect, - "far": self.far, - "filmGauge": self.film_gauge, - "filmOffset": self.film_offset, - "focus": self.focus, - "fov": self.fov, - "near": self.near, - "zoom": self.zoom, - } - } - return data - - -def item_size(array: np.ndarray) -> int: - """Returns the size of the 1 or 2d numpy array - - Args: - array: np array for which to get the size of - - Raises: - ValueError: if n dimensions is not == 1 or 2 - """ - if array.ndim == 1: - return 1 - if array.ndim == 2: - return array.shape[0] - raise ValueError(f"I can only pack 1- or 2-dimensional numpy arrays, but this one has {array.ndim:d} dimensions") - - -def threejs_type(dtype: type) -> Tuple[str, int]: - """Converts the np type to 3js type, returns str name and hex of equivalent 3js type - - Args: - dtype: np type - - Raises: - ValueError: if unsupported datatype - """ - if dtype == np.uint8: - return "Uint8Array", 0x12 - if dtype == np.int32: - return "Int32Array", 0x15 - if dtype == np.uint32: - return "Uint32Array", 0x16 - if dtype == np.float32: - return "Float32Array", 0x17 - - raise ValueError("Unsupported datatype: " + str(dtype)) - - -def pack_numpy_array(x: np.ndarray) -> Dict[str, Any]: - """convert numpy array to byte-based message with context, returns serialized message - - Args: - x: array to pack into message format - """ - if x.dtype == np.float64: - x = x.astype(np.float32) - typename, extcode = threejs_type(x.dtype) - return { - "itemSize": item_size(x), - "type": typename, - "array": umsgpack.Ext(extcode, x.tobytes("F")), - "normalized": False, - } - - -def data_from_stream(stream: BinaryIO) -> str: - """Read in data from some binary io stream and returns the serialized stream - - Args: - stream: the stream in which data is being sent over - - Raises: - ValueError: invalid stream type - """ - if isinstance(stream, BytesIO): - data = stream.read().decode(encoding="utf-8") - elif isinstance(stream, StringIO): - data = stream.read() - else: - raise ValueError(f"Stream must be instance of StringIO or BytesIO, not {type(stream)}") - return data - - -class MeshGeometry(Geometry): - """Basic mesh geometry class - - Args: - contents: the stream of contents representing the mesh - mesh_format: specification of mesh format - """ - - def __init__(self, contents: bytearray, mesh_format: str): - super().__init__() - self.contents = contents - self.mesh_format = mesh_format - - def lower(self, object_data): - return {"type": "_meshfile", "uuid": self.uuid, "format": self.mesh_format, "data": self.contents} - - -class ObjMeshGeometry(MeshGeometry): - """Basic mesh geometry class - - Args: - contents: the stream of contents representing the mesh - """ - - def __init__(self, contents: bytearray): - super().__init__(contents, "obj") - - @staticmethod - def from_file(fname: str) -> MeshGeometry: - """Reading mesh geometry from file. Returns mesh geometry object - - Args: - fname: file name to read from - """ - with open(fname, encoding="utf8") as f: - return MeshGeometry(f.read(), "obj") - - @staticmethod - def from_stream(f: BinaryIO) -> MeshGeometry: - """Reads mesh geometry from io stream. Returns mesh geometry object - - Args: - f: binary io stream to read from - """ - return MeshGeometry(data_from_stream(f), "obj") - - -class DaeMeshGeometry(MeshGeometry): - """Basic DAE mesh geometry class - - Args: - contents: the stream of contents representing the mesh - """ - - def __init__(self, contents: bytearray): - super().__init__(contents, "dae") - - @staticmethod - def from_file(fname: str) -> MeshGeometry: - """Reading mesh geometry from file. Returns mesh geometry object - - Args: - fname: file name to read from - """ - with open(fname, encoding="utf8") as f: - return MeshGeometry(f.read(), "dae") - - @staticmethod - def from_stream(f: BinaryIO) -> MeshGeometry: - """Reads mesh geometry from io stream. Returns mesh geometry object - - Args: - f: binary io stream to read from - """ - return MeshGeometry(data_from_stream(f), "dae") - - -class StlMeshGeometry(MeshGeometry): - """Basic STL mesh geometry class - - Args: - contents: the stream of contents representing the mesh - """ - - def __init__(self, contents: bytearray): - super().__init__(contents, "stl") - - @staticmethod - def from_file(fname: str) -> MeshGeometry: - """Reading mesh geometry from file. Returns mesh geometry object - - Args: - fname: file name to read from - """ - with open(fname, "rb") as f: - arr = np.frombuffer(f.read(), dtype=np.uint8) - _, extcode = threejs_type(np.uint8) - encoded = umsgpack.Ext(extcode, arr.tobytes()) - return MeshGeometry(encoded, "stl") - - @staticmethod - def from_stream(f: BinaryIO) -> MeshGeometry: - """Reads mesh geometry from io stream. Returns mesh geometry object - - Args: - f: binary io stream to read from - """ - if isinstance(f, BytesIO): - arr = np.frombuffer(f.read(), dtype=np.uint8) - elif isinstance(f, StringIO): - arr = np.frombuffer(bytes(f.read(), "utf-8"), dtype=np.uint8) - else: - raise ValueError(f"Stream must be instance of StringIO or BytesIO, not {type(f)}") - _, extcode = threejs_type(np.uint8) - encoded = umsgpack.Ext(extcode, arr.tobytes()) - return MeshGeometry(encoded, "stl") - - -class PlyMeshGeometry(MeshGeometry): - """Basic PLY mesh geometry class - - Args: - contents: the stream of contents representing the mesh - """ - - def __init__(self, contents: bytearray): - super().__init__(contents, "ply") - - @staticmethod - def from_file(fname: str) -> MeshGeometry: - """Reading mesh geometry from file. Returns mesh geometry object - - Args: - fname: file name to read from - """ - with open(fname, "rb") as f: - arr = np.frombuffer(f.read(), dtype=np.uint8) - _, extcode = threejs_type(np.uint8) - encoded = umsgpack.Ext(extcode, arr.tobytes()) - return MeshGeometry(encoded, "ply") - - @staticmethod - def from_stream(f: BinaryIO) -> MeshGeometry: - """Reads mesh geometry from io stream. Returns mesh geometry object - - Args: - f: binary io stream to read from - """ - if isinstance(f, BytesIO): - arr = np.frombuffer(f.read(), dtype=np.uint8) - elif isinstance(f, StringIO): - arr = np.frombuffer(bytes(f.read(), "utf-8"), dtype=np.uint8) - else: - raise ValueError(f"Stream must be instance of StringIO or BytesIO, not {type(f)}") - _, extcode = threejs_type(np.uint8) - encoded = umsgpack.Ext(extcode, arr.tobytes()) - return MeshGeometry(encoded, "stl") - - -class TriangularMeshGeometry(Geometry): - """ - A mesh consisting of an arbitrary collection of triangular faces. - To construct one, you need to pass in a collection of vertices as an Nx3 array - and a collection of faces as an Mx3 array. Each element of `faces` - should be a collection of 3 indices into the `vertices` array. - For example, to create a square made out of two adjacent triangles, we could do: - vertices = np.array([ - [0, 0, 0], # the first vertex is at [0, 0, 0] - [1, 0, 0], - [1, 0, 1], - [0, 0, 1] - ]) - faces = np.array([ - [0, 1, 2], # The first face consists of vertices 0, 1, and 2 - [3, 0, 2] - ]) - mesh = TriangularMeshGeometry(vertices, faces) - """ - - __slots__ = ["vertices", "faces"] - - def __init__(self, vertices: np.ndarray, faces: np.ndarray): - super().__init__() - - vertices = np.asarray(vertices, dtype=np.float32) - faces = np.asarray(faces, dtype=np.uint32) - assert vertices.shape[1] == 3, "`vertices` must be an Nx3 array" - assert faces.shape[1] == 3, "`faces` must be an Mx3 array" - self.vertices = vertices - self.faces = faces - - def lower(self, object_data) -> Dict[str, Any]: - return { - "uuid": self.uuid, - "type": "BufferGeometry", - "data": { - "attributes": {"position": pack_numpy_array(self.vertices.T)}, - "index": pack_numpy_array(self.faces.T), - }, - } - - -class PointsGeometry(Geometry): - """Points Geometry class - - Args: - position: position of the point to draw - color: color to render the point - """ - - def __init__(self, position: np.ndarray, color: Optional[np.ndarray] = None): - super().__init__() - self.position = position - self.color = color - - def lower(self, object_data) -> Dict[str, Any]: - attrs = {"position": pack_numpy_array(self.position)} - if self.color is not None: - attrs["color"] = pack_numpy_array(self.color) - return {"uuid": self.uuid, "type": "BufferGeometry", "data": {"attributes": attrs}} - - -class PointsMaterial(Material): - """Points Material class - - Args: - size: size of point to render - color: color to render the point - sizeAttenuation: whether to attenuate size - """ - - def __init__(self, size: float = 0.001, color: int = 0xFFFFFF, size_attenuation: bool = True): - super().__init__() - self.size = size - self.color = color - self.size_attenuation = size_attenuation - - def lower(self, object_data) -> Dict[str, Any]: - return { - "uuid": self.uuid, - "type": "PointsMaterial", - "color": self.color, - "size": self.size, - "sizeAttenuation": bool(self.size_attenuation), - "vertexColors": 2, - } - - -class Points(Object): - """Points object class""" - - _type = "Points" - - -def PointCloud(position: np.ndarray, color: np.ndarray, **kwargs) -> Points: # pylint: disable=invalid-name - """Creates a point cloud with the specified position of points - - Args: - position: position of points - color: color to render point cloud - """ - return Points(PointsGeometry(position, color), PointsMaterial(**kwargs)) - - -class Line(Object): - """Line class""" - - _type = "Line" - - -class LineSegments(Object): - """Line Segment class""" - - _type = "LineSegments" - - -class LineLoop(Object): - """Line Loop class""" - - _type = "LineLoop" - - -def triad(scale: float = 1.0) -> LineSegments: - """ - A visual representation of the origin of a coordinate system, drawn as three - lines in red, green, and blue along the x, y, and z axes. The `scale` parameter - controls the length of the three lines. - Returns an `Object` which can be passed to `set_object()` - - Args: - scale: scale size of triad - """ - return LineSegments( - PointsGeometry( - position=np.array([[0, 0, 0], [scale, 0, 0], [0, 0, 0], [0, scale, 0], [0, 0, 0], [0, 0, scale]]) - .astype(np.float32) - .T, - color=np.array([[1, 0, 0], [1, 0.6, 0], [0, 1, 0], [0.6, 1, 0], [0, 0, 1], [0, 0.6, 1]]) - .astype(np.float32) - .T, - ), - LineBasicMaterial(vertexColors=True), - ) - - -def camera(scale: float = 1.0) -> LineSegments: - """ - A visual representation of the origin of a coordinate system, drawn as three - lines in red, green, and blue along the x, y, and z axes. The `scale` parameter - controls the length of the three lines. - Returns an `Object` which can be passed to `set_object()` - - Args: - scale: scale size of camera - """ - return LineSegments( - PointsGeometry( - position=np.array([[0, 0, 0], [scale, 0, 0], [0, 0, 0], [0, scale, 0], [0, 0, 0], [0, 0, scale]]) - .astype(np.float32) - .T, - color=np.array([[1, 0, 0], [1, 0.6, 0], [0, 1, 0], [0.6, 1, 0], [0, 0, 1], [0, 0.6, 1]]) - .astype(np.float32) - .T, - ), - LineBasicMaterial(vertexColors=True), - ) diff --git a/nerfactory/viewer/server/path.py b/nerfactory/viewer/server/path.py index ca80c5d29..394f019c6 100644 --- a/nerfactory/viewer/server/path.py +++ b/nerfactory/viewer/server/path.py @@ -16,7 +16,6 @@ """ -import sys from typing import Tuple UNICODE = str diff --git a/nerfactory/viewer/server/server.py b/nerfactory/viewer/server/server.py index e37ccba24..312e1939d 100644 --- a/nerfactory/viewer/server/server.py +++ b/nerfactory/viewer/server/server.py @@ -16,7 +16,7 @@ import sys -from typing import Callable, List, Optional +from typing import Callable, List, Optional, Tuple import msgpack import msgpack_numpy @@ -31,24 +31,14 @@ from aiortc import RTCPeerConnection, RTCSessionDescription from aiortc.rtcrtpsender import RTCRtpSender from zmq.eventloop.zmqstream import ZMQStream -from nerfactory.viewer.server.tree import SceneTree, find_node, walk +from nerfactory.viewer.server.state.node import find_node, get_tree, walk +from nerfactory.viewer.server.state.state_node import StateNode from nerfactory.viewer.server.video_stream import SingleFrameStreamTrack MAX_ATTEMPTS = 1000 DEFAULT_ZMQ_METHOD = "tcp" DEFAULT_ZMQ_PORT = 6000 DEFAULT_WEBSOCKET_PORT = 8051 -WEBSOCKET_COMMANDS = [ - "set_transform", - "set_object", - "set_output_options", - "set_output_type", - "set_training_state", - "get_object", - "set_property", - "delete", -] -WEBRTC_COMMANDS = ["set_image"] def find_available_port(func: Callable, default_port: int, max_attempts: int = MAX_ATTEMPTS, **kwargs) -> None: @@ -105,7 +95,8 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): # pylint: disable=a self.bridge.send_scene(self) async def on_message(self, message: bytearray): # pylint: disable=invalid-overridden-method - """On reception of message, parses the message and calls the appropriate function based on the type of command + """On reception of message from the websocket, + parses the message and calls the appropriate function based on the type of command Args: message: byte message to parse @@ -115,22 +106,23 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): # pylint: disable=a type_ = m["type"] path = list(filter(lambda x: len(x) > 0, m["path"].split("/"))) - if type_ == "set_transform": - find_node(self.bridge.tree, path).transform = data - elif type_ == "set_object": - find_node(self.bridge.tree, path).object = data - find_node(self.bridge.tree, path).properties = [] - elif type_ == "set_output_options": - find_node(self.bridge.tree, path).object = data - elif type_ == "set_output_type": - find_node(self.bridge.tree, path).object = data - elif type_ == "set_max_resolution": - find_node(self.bridge.tree, path).object = data - elif type_ == "set_min_resolution": - find_node(self.bridge.tree, path).object = data - elif type_ == "set_training_state": - find_node(self.bridge.tree, path).object = data + if type_ == "write": + # writes the data coming from the websocket + find_node(self.bridge.state_tree, path).data = m["data"] + if m["path"] != "renderingState/camera": + # dispatch to the websockets if not a camera update! + # TODO(ethan): handle the camera properly! + # TODO: but don't update the current websocket + command = {"type": "write", "path": m["path"], "data": m["data"]} + packed_data = umsgpack.packb(command) + frames = ["write".encode("utf-8"), m["path"].encode("utf-8"), packed_data] + self.bridge.forward_to_websockets(frames, websocket_to_skip=self) + elif type_ == "read": + # reads and returns the data + data = find_node(self.bridge.state_tree, path).data + self.write_message(data, binary=True) elif type_ == "offer": + # returns the description to for WebRTC to the specific websocket connection offer = RTCSessionDescription(m["data"]["sdp"], m["data"]["type"]) pc = RTCPeerConnection() @@ -139,7 +131,7 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): # pylint: disable=a video = SingleFrameStreamTrack() self.bridge.video_tracks.add(video) _ = pc.addTrack(video) - # TODO(eventually do something with the codec) + # TODO: do something with the codec # video_sender = pc.addTrack(video) # force_codec(pc, video_sender, video_codec) @@ -154,6 +146,14 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): # pylint: disable=a } data = umsgpack.packb(cmd_data) self.write_message(data, binary=True) + else: + cmd_data = { + "type": "error", + "path": "", + "data": {"error": "Unknown command type: " + type_}, + } + data = umsgpack.packb(cmd_data) + self.write_message(data, binary=True) def on_close(self): self.bridge.websocket_pool.remove(self) @@ -196,7 +196,7 @@ class ZMQWebSocketBridge: self.app.listen(websocket_port, **listen_kwargs) self.websocket_port = websocket_port - self.tree = SceneTree() + self.state_tree = get_tree(StateNode) def __str__(self) -> str: class_name = self.__class__.__name__ @@ -212,61 +212,37 @@ class ZMQWebSocketBridge: Args: frames: the list containing command + object to be placed in tree """ - cmd = frames[0].decode("utf-8") - print(cmd) if len(frames) != 3: self.zmq_socket.send(b"error: expected 3 frames") return + type_ = frames[0].decode("utf-8") path = list(filter(lambda x: len(x) > 0, frames[1].decode("utf-8").split("/"))) data = frames[2] - if cmd in WEBSOCKET_COMMANDS: - if cmd != "get_object": - self.forward_to_websockets(frames) - if cmd == "set_transform": - find_node(self.tree, path).transform = data - elif cmd == "set_object": - find_node(self.tree, path).object = data - find_node(self.tree, path).properties = [] - elif cmd == "set_output_options": - find_node(self.tree, path).object = data - elif cmd == "set_output_type": - find_node(self.tree, path).object = data - elif cmd == "set_max_resolution": - find_node(self.tree, path).object = data - elif cmd == "set_min_resolution": - find_node(self.tree, path).object = data - elif cmd == "set_training_state": - find_node(self.tree, path).object = data - elif cmd == "get_object": - data = find_node(self.tree, path).object - if isinstance(data, type(None)): - data = umsgpack.packb("error: object not found") - self.zmq_socket.send(data) - return - elif cmd == "set_property": - find_node(self.tree, path).properties.append(data) - elif cmd == "delete": - if len(path) > 0: - parent = find_node(self.tree, path[:-1]) - child = path[-1] - if child in parent: - del parent[child] - else: - self.tree = SceneTree() - elif cmd in WEBRTC_COMMANDS: - if cmd == "set_image": - image = msgpack.unpackb( - data, object_hook=msgpack_numpy.decode, use_list=False, max_bin_len=50000000, raw=False - ) - for video_track in self.video_tracks: - video_track.put_frame(image) - else: - self.zmq_socket.send(b"error: unknown command") - return - self.zmq_socket.send(b"ok") - return - def forward_to_websockets(self, frames: List[bytes]): + if type_ == "write": + # TODO: use state_tree name + unpacked_data = umsgpack.unpackb(data) + find_node(self.state_tree, path).data = unpacked_data["data"] + self.forward_to_websockets(frames) + self.zmq_socket.send(umsgpack.packb(b"ok")) + elif type_ == "read": + # walk the node from the specified to get the full state dictionary + # TODO(ethan): handle the "data" key... + read_data = find_node(self.state_tree, path).data + self.zmq_socket.send(umsgpack.packb(read_data)) + elif type_ == "set_image": + image = msgpack.unpackb( + data, object_hook=msgpack_numpy.decode, use_list=False, max_bin_len=50000000, raw=False + ) + for video_track in self.video_tracks: + video_track.put_frame(image) + self.zmq_socket.send(umsgpack.packb(b"ok")) + else: + self.zmq_socket.send(umsgpack.packb(b"error: unknown command")) + + def forward_to_websockets( + self, frames: Tuple[str, str, bytes], websocket_to_skip: Optional[WebSocketHandler] = None + ): """Forward a zmq message to all websockets. Args: @@ -274,7 +250,10 @@ class ZMQWebSocketBridge: """ _, _, data = frames # cmd, path, data for websocket in self.websocket_pool: - websocket.write_message(data, binary=True) + if websocket_to_skip and websocket == websocket_to_skip: + pass + else: + websocket.write_message(data, binary=True) def setup_zmq(self, url: str): """Setup a zmq socket and connect it to the given url. @@ -294,13 +273,11 @@ class ZMQWebSocketBridge: Args: websocket: websocket to send information over """ - for node in walk(self.tree): - if node.object is not None: - websocket.write_message(node.object, binary=True) - for p in node.properties: - websocket.write_message(p, binary=True) - if node.transform is not None: - websocket.write_message(node.transform, binary=True) + print("Sending entire scene state on websocket connect (i.e,. a page refresh).") + for path, node in walk("", self.state_tree): + if node.data is not None: + command = {"type": "write", "path": path, "data": node.data} + websocket.write_message(umsgpack.packb(command), binary=True) def run(self): """starts and runs the websocket bridge""" diff --git a/nerfactory/viewer/server/tree.py b/nerfactory/viewer/server/state/node.py similarity index 56% rename from nerfactory/viewer/server/tree.py rename to nerfactory/viewer/server/state/node.py index 1eacaa766..e63afa689 100644 --- a/nerfactory/viewer/server/tree.py +++ b/nerfactory/viewer/server/state/node.py @@ -13,49 +13,53 @@ # limitations under the License. """ -Functions for traversing server bridge tree nodes (storage) +For tree logic code. """ - from collections import defaultdict -from typing import List +from typing import Callable -class TreeNode(defaultdict): - """Tree Node object""" - - __slots__ = ["object", "transform", "properties", "animation"] +class Node(defaultdict): + """ + The base class Node. + """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.object = None - self.properties = [] - self.transform = None - self.animation = None -SceneTree = lambda: TreeNode(SceneTree) - - -def walk(tree: TreeNode) -> None: - """Walk the entire tree and return the values - - Args: - tree: the root of the tree to start search +def get_tree(node_class: Callable) -> Callable: """ - yield tree - for v in tree.values(): - yield from walk(v) - - -def find_node(tree: TreeNode, path: List[str]) -> TreeNode: - """Find the node associated with the path - - Args: - tree: the root of the tree to start search - path: the path we are searching for + Get a tree from a node class. + This allows one to do tree["path"]["to"]["node"] + and it will return a new node if it doesn't exist + or the current node if it does. """ + assert isinstance(node_class(), Node) + tree = lambda: node_class(tree) + return tree() + + +def find_node(tree, path): if len(path) == 0: return tree else: return find_node(tree[path[0]], path[1:]) + + +def set_node_value(tree, path, value): + if len(path) == 0: + tree.data = value + else: + set_node_value(tree[path[0]], path[1:], value) + + +def walk(path, tree): + """Walk the entire tree and return the values + Args: + tree: the root of the tree to start search + """ + yield path, tree + for k, v in tree.items(): + yield from walk(path + "/" + k, v) diff --git a/nerfactory/viewer/server/state/state_node.py b/nerfactory/viewer/server/state/state_node.py new file mode 100644 index 000000000..5ef246510 --- /dev/null +++ b/nerfactory/viewer/server/state/state_node.py @@ -0,0 +1,28 @@ +# Copyright 2022 The Plenoptix Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import List + +from nerfactory.viewer.server.state.node import Node + + +class StateNode(Node): + """Node that holds a hierarchy of state nodes""" + + __slots__ = ["data"] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.path = None + self.data = None diff --git a/nerfactory/viewer/server/transformations.py b/nerfactory/viewer/server/transformations.py deleted file mode 100644 index e2a0c6442..000000000 --- a/nerfactory/viewer/server/transformations.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright 2022 The Plenoptix Team. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Some helpful functions for creating geometric transformations. -""" - -import math -from typing import List, Optional - -import numpy as np - - -def get_unit_vector( - data: List[float], axis: Optional[List[float]] = None, out: Optional[List[float]] = None -) -> Optional[np.ndarray]: - """Return ndarray normalized by length, i.e. Euclidean norm, along axis. - - Args: - data: direction in which we want to calculate the unit vector for - axis: the axis along which to get the unit vector - out: buffer in which we write the unit vector out to (if not returning) - """ - if out is None: - data = np.array(data, dtype=np.float64, copy=True) - if data.ndim == 1: - data /= math.sqrt(np.dot(data, data)) - return data - else: - if out is not data: - out[:] = np.array(data, copy=False) - data = out - length = np.atleast_1d(np.sum(data * data, axis)) - np.sqrt(length, length) - if axis is not None: - length = np.expand_dims(length, axis) - data /= length - if out is None: - return data - return None - - -def get_translation_matrix(direction: List[float]) -> np.ndarray: - """Return matrix to translate by direction vector. - - Args: - direction: specifying direction of translation - """ - M = np.identity(4) - M[:3, 3] = direction[:3] - return M - - -def get_rotation_matrix(angle: float, direction: List[float], point: Optional[List[float]] = None) -> np.ndarray: - """Return matrix to rotate about axis defined by point and direction. - - Args: - angle: angle of rotation - direction: specifying axis of rotation - point: if specified, rotation not around origin - """ - sina = math.sin(angle) - cosa = math.cos(angle) - direction = get_unit_vector(direction[:3]) - # rotation matrix around unit vector - R = np.diag([cosa, cosa, cosa]) - R += np.outer(direction, direction) * (1.0 - cosa) - direction *= sina - R += np.array( - [[0.0, -direction[2], direction[1]], [direction[2], 0.0, -direction[0]], [-direction[1], direction[0], 0.0]] - ) - M = np.identity(4) - M[:3, :3] = R - if point is not None: - # rotation not around origin - point = np.array(point[:3], dtype=np.float64, copy=False) - M[:3, 3] = point - np.dot(R, point) - return M diff --git a/nerfactory/viewer/server/utils.py b/nerfactory/viewer/server/utils.py index b769190bd..e13fc91c1 100644 --- a/nerfactory/viewer/server/utils.py +++ b/nerfactory/viewer/server/utils.py @@ -20,13 +20,6 @@ from typing import Any, Dict, List, Optional, Tuple import numpy as np import torch -from nerfactory.cameras.cameras import ( - Camera, - get_camera, - get_intrinsics_from_intrinsics_matrix, -) -from nerfactory.viewer.server.visualizer import Viewer - def get_chunks( lst: List[float], num_chunks: Optional[int] = None, size_of_chunk: Optional[int] = None @@ -80,25 +73,3 @@ def get_intrinsics_matrix_and_camera_to_world_h( ) return intrinsics_matrix, camera_to_world_h - - -def get_camera_from_vis(vis: Viewer, name: str = "/Cameras/Main Camera", image_height: int = 100) -> Camera: - """Caculates the specified camera object's parameters (extrinsics/intrinsics); returns associated Camera Object. - - Args: - vis: viewer object - name: name of camera to calculate for. Defaults to "/Cameras/Main Camera". - image_height: height of image. Defaults to 100. - """ - data = vis[name].get_object() - if data is None: - return None - camera_object = data["object"]["object"] - intrinsics_matrix, camera_to_world_h = get_intrinsics_matrix_and_camera_to_world_h( - camera_object, image_height=image_height - ) - - camera_to_world = camera_to_world_h[:3, :] - intrinsics = get_intrinsics_from_intrinsics_matrix(intrinsics_matrix) - camera = get_camera(intrinsics, camera_to_world) - return camera diff --git a/nerfactory/viewer/server/video_stream.py b/nerfactory/viewer/server/video_stream.py index c64c6518c..fb177e7b0 100644 --- a/nerfactory/viewer/server/video_stream.py +++ b/nerfactory/viewer/server/video_stream.py @@ -79,7 +79,7 @@ class SingleFrameStreamTrack(VideoStreamTrack): def __init__(self): super().__init__() - self.background_frame = np.zeros((480, 640, 3), dtype="uint8") * 255 + self.background_frame = np.ones((480, 640, 3), dtype="uint8") * 255 # white background self.frame = None self.put_frame(self.background_frame) diff --git a/nerfactory/viewer/server/viewer_utils.py b/nerfactory/viewer/server/viewer_utils.py index 1cd02c356..add27f881 100644 --- a/nerfactory/viewer/server/viewer_utils.py +++ b/nerfactory/viewer/server/viewer_utils.py @@ -15,24 +15,16 @@ """Code to interface with the `vis/` (the JS visualizer). """ -import copy import logging -import random import sys import threading import time -from typing import Any, Dict, List, Optional +from typing import Any, Dict import numpy as np import torch -import nerfactory.viewer.server.cameras as c -import nerfactory.viewer.server.geometry as g -from nerfactory.cameras.cameras import ( - Camera, - get_camera, - get_intrinsics_from_intrinsics_matrix, -) +from nerfactory.cameras.cameras import get_camera, get_intrinsics_from_intrinsics_matrix from nerfactory.cameras.rays import RayBundle from nerfactory.data.image_dataset import ImageDataset from nerfactory.data.structs import DatasetInputs @@ -40,7 +32,6 @@ from nerfactory.graphs.base import Graph from nerfactory.utils import profiler from nerfactory.utils.config import ViewerConfig from nerfactory.utils.decorators import check_visualizer_enabled, decorate_all -from nerfactory.viewer.server.transformations import get_translation_matrix from nerfactory.viewer.server.utils import get_intrinsics_matrix_and_camera_to_world_h from nerfactory.viewer.server.visualizer import Viewer @@ -117,15 +108,17 @@ class CheckThread(threading.Thread): self.state = state def run(self): - """Run function that checks to see if any of the existing state has changed (e.g. camera pose/output type/resolutions). - Sets the visualizer state flag to true to signal to render thread that an interrupt was registered. + """Run function that checks to see if any of the existing state has changed + (e.g. camera pose/output type/resolutions). + Sets the visualizer state flag to true to signal + to render thread that an interrupt was registered. """ self.state.check_done_render = False while not self.state.check_done_render: # check camera - data = self.state.vis["/Cameras/Main Camera"].get_object() + data = self.state.vis["renderingState/camera"].read() if data is not None: - camera_object = data["object"]["object"] + camera_object = data["object"] if self.state.prev_camera_matrix is None or not np.allclose( camera_object["matrix"], self.state.prev_camera_matrix ): @@ -133,27 +126,23 @@ class CheckThread(threading.Thread): return # check output type - data = self.state.vis["/Output Type"].get_object() - if data is None: + output_type = self.state.vis["renderingState/output_choice"].read() + if output_type is None: output_type = "default" - else: - output_type = data["output_type"] if self.state.prev_output_type != output_type: self.state.check_interrupt_vis = True return # check max render - data = self.state.vis["/Max Resolution"].get_object() - if data is not None: - max_resolution = int(data["max_resolution"]) + max_resolution = self.state.vis["renderingState/maxResolution"].read() + if max_resolution is not None: if self.state.max_resolution != max_resolution: self.state.check_interrupt_vis = True return # check min render - data = self.state.vis["/Min Resolution"].get_object() - if data is not None: - min_resolution = int(data["min_resolution"]) + min_resolution = self.state.vis["renderingState/minResolution"].read() + if min_resolution is not None: if self.state.min_resolution != min_resolution: self.state.check_interrupt_vis = True return @@ -174,7 +163,6 @@ class VisualizerState: if self.config.enable: zmq_url = self.config.zmq_url self.vis = Viewer(zmq_url=zmq_url) - self.vis.delete() else: logging.info("Continuing without viewer.") @@ -198,28 +186,29 @@ class VisualizerState: image_dataset: dataset to render in the scene dataset_inputs: inputs to the image dataset and ray generator """ - indices = random.sample(range(len(image_dataset)), k=10) - for idx in indices: + + # clear the current scene + self.vis["sceneState/sceneBounds"].delete() + self.vis["sceneState/cameras"].delete() + + # draw the training cameras and images + image_indices = range(len(image_dataset)) + for idx in image_indices: image = image_dataset[idx]["image"] camera = get_camera(dataset_inputs.intrinsics[idx], dataset_inputs.camera_to_world[idx], None) - pose = camera.get_camera_to_world().double().numpy() - K = camera.get_intrinsics_matrix().double().numpy() - draw_camera_frustum( - self.vis, - image=(image.double().numpy() * 255.0), - pose=pose, - K=K, - height=1.0, - name=f"image_dataset/{idx:06d}", - displayed_focal_length=0.5, - realistic=False, - ) - aabb = dataset_inputs.scene_bounds.aabb - draw_aabb(self.vis, aabb, name="dataset_inputs_train/scene_bounds/aabb") + bgr = image[..., [2, 1, 0]] + self.vis[f"sceneState/cameras/{idx:06d}"].write(camera.to_json(image=bgr, resize_shape=(100, 100))) + + # draw the scene bounds (i.e., the bounding box) + json_ = dataset_inputs.scene_bounds.to_json() + self.vis["sceneState/sceneBounds"].write(json_) + + # set the properties of the camera + # self.vis["renderingState/camera"].write(json_) # set the main camera intrinsics to one from the dataset - K = camera.get_intrinsics_matrix() - set_persp_intrinsics_matrix(self.vis, K.double().numpy()) + # K = camera.get_intrinsics_matrix() + # set_persp_intrinsics_matrix(self.vis, K.double().numpy()) def update_scene(self, step: int, graph: Graph) -> None: """updates the scene based on the graph weights @@ -228,21 +217,22 @@ class VisualizerState: step: iteration step of training graph: the current checkpoint of the model """ - data = self.vis["/Training State"].get_object() - if data is None or not data["training_state"]: + is_training = self.vis["renderingState/isTraining"].read() + + if is_training is None or is_training: # in training mode, render every few steps if self._is_render_step(step): self._render_image_in_viewer(graph) else: # in pause training mode, enter render loop with set graph local_step = step - run_loop = data["training_state"] + run_loop = not is_training while run_loop: if self._is_render_step(local_step): self._render_image_in_viewer(graph) - data = self.vis["/Training State"].get_object() - run_loop = data["training_state"] + is_training = self.vis["renderingState/isTraining"].read() + run_loop = not is_training local_step += 1 def check_interrupt(self, frame, event, arg): # pylint: disable=unused-argument @@ -279,14 +269,15 @@ class VisualizerState: outputs: the dictionary of outputs to choose from, from the graph """ if not self.outputs_set: - set_output_options(self.vis, list(outputs.keys())) + # set_output_options(self.vis, list(outputs.keys())) + self.vis["renderingState/output_options"].write(list(outputs.keys())) self.outputs_set = True # gross hack to get the image key, depending on which keys the graph uses if self.prev_output_type == "default": self.prev_output_type = "rgb" if "rgb" in outputs else "rgb_fine" image_output = outputs[self.prev_output_type].cpu().numpy() * 255 image = (image_output).astype("uint8") - self.vis["/Cameras/Main Camera"].set_image(image) + self.vis.set_image(image) @profiler.time_function def _render_image_in_viewer(self, graph: Graph) -> None: @@ -295,10 +286,10 @@ class VisualizerState: The image is sent of a TCP connection and then uses WebRTC to send it to the viewer. """ # check and perform camera updates - data = self.vis["/Cameras/Main Camera"].get_object() + data = self.vis["renderingState/camera"].read() if data is None: return - camera_object = data["object"]["object"] + camera_object = data["object"] # hacky way to prevent overflow check to see if < 100; TODO(make less hacky) if self.prev_camera_matrix is not None and np.allclose(camera_object["matrix"], self.prev_camera_matrix): self.res_upscale_factor = min(self.res_upscale_factor * 2, 100) @@ -307,21 +298,19 @@ class VisualizerState: self.res_upscale_factor = 1 # check and perform output type updates - data = self.vis["/Output Type"].get_object() - if data is None: + output_type = self.vis["renderingState/output_choice"].read() + if output_type is None: output_type = "default" - else: - output_type = data["output_type"] self.prev_output_type = output_type # check and perform min/max update - data = self.vis["/Max Resolution"].get_object() - if data is not None: - self.max_resolution = int(data["max_resolution"]) + max_resolution = self.vis["renderingState/maxResolution"].read() + if max_resolution: + self.max_resolution = max_resolution - data = self.vis["/Min Resolution"].get_object() - if data is not None: - self.min_resolution = int(data["min_resolution"]) + min_resolution = self.vis["renderingState/minResolution"].read() + if min_resolution: + self.min_resolution = min_resolution image_height = min( self.min_resolution * self.res_upscale_factor, @@ -368,191 +357,3 @@ def get_default_vis() -> Viewer: zmq_url = "tcp://0.0.0.0:6000" viewer = Viewer(zmq_url=zmq_url) return viewer - - -def set_output_options(vis: Viewer, output_options: List[str]): - """Sets the possible list of output options for user to toggle - - Args: - vis: current Viewer - output_options: list of possible output types to select from - """ - vis["output_options"].set_output_options(output_options) - - -def show_box_test(vis: Viewer): - """Simple test to draw a box and make sure everything is working. - - Args: - vis: current Viewer - """ - vis["box"].set_object(g.Box([1.0, 1.0, 1.0]), material=g.MeshPhongMaterial(color=0xFF0000)) - - -def show_ply(vis: Viewer, ply_path: str, name: str = "ply", color: Optional[int] = None): - """Show the PLY file in the 3D viewer. Specify the full filename as input. - - Args: - vis: current Viewer - ply_path: path to ply to load in - name: reference name within viewer storage - color: color to render the ply in - """ - assert ply_path.endswith(".ply") - if color: - material = g.MeshPhongMaterial(color=color) - else: - material = g.MeshPhongMaterial(vertexColors=True) - vis[name].set_object(g.PlyMeshGeometry.from_file(ply_path), material) - - -def show_obj(vis: Viewer, obj_path: str, name: str = "obj", color: Optional[int] = None): - """Show the PLY file in the 3D viewer. Specify the full filename as input. - - Args: - vis: current Viewer - obj_path: path to obj to load in - name: reference name within viewer storage - color: color to render the obj in - """ - assert obj_path.endswith(".obj") - if color: - material = g.MeshPhongMaterial(color=color) - else: - material = g.MeshPhongMaterial(vertexColors=True) - vis[name].set_object(g.ObjMeshGeometry.from_file(obj_path), material) - - -def draw_camera_frustum( - vis: Viewer, - image: np.ndarray = np.random.rand(100, 100, 3) * 255.0, - pose: np.ndarray = get_translation_matrix([0, 0, 0]), - K: Optional[np.ndarray] = None, - name: str = "0000000", - displayed_focal_length: Optional[float] = None, - shift_forward: Optional[float] = None, - height: Optional[float] = None, - realistic: bool = True, -): - """Draw the camera in the scene. - - Args: - vis: current Viewer - image: image associated with the current camera - pose: pose of the camera - K: camera matrix - name: reference name within viewer storage - displayed_focal_length: the focal length to be displayed - shift_forward: how much to shift the drawn camera forward - height: height of the associated image plane - realistic: whether to render the image plane at the "realistic" focal length - """ - - assert K[0, 0] == K[1, 1] - focal_length = K[0, 0] - pp_w = K[0, 2] - pp_h = K[1, 2] - - if displayed_focal_length: - assert height is None or not realistic - if height: - assert displayed_focal_length is None or not realistic - - if height: - dfl = height / (2.0 * (pp_h / focal_length)) - width = 2.0 * (pp_w / focal_length) * dfl - if displayed_focal_length is None: - displayed_focal_length = dfl - elif displayed_focal_length: - width = 2.0 * (pp_w / focal_length) * displayed_focal_length - height = 2.0 * (pp_h / focal_length) * displayed_focal_length - else: - assert not realistic - - if pose.shape == (3, 4): - pose = np.concatenate([pose, np.zeros_like(pose[:1])], axis=0) - pose[3, 3] = 1.0 - - # draw the frustum - g_frustum = c.frustum(scale=1.0, focal_length=displayed_focal_length, width=width, height=height) - vis[name + "/frustum"].set_object(g_frustum) - if not realistic: - vis[name + "/frustum"].set_transform(get_translation_matrix([0, 0, displayed_focal_length])) - - # draw the image plane - g_image_plane = c.ImagePlane(image, width=width, height=height) - vis[name + "/image_plane"].set_object(g_image_plane) - if realistic: - assert displayed_focal_length is not None, "Need to set displayed focal length" - vis[name + "/image_plane"].set_transform(get_translation_matrix([0, 0, -1.0 * displayed_focal_length])) - - if shift_forward: - matrix = get_translation_matrix([0, 0, displayed_focal_length]) - matrix2 = get_translation_matrix([0, 0, -1.0 * shift_forward]) - vis[name + "/frustum"].set_transform(matrix2 @ matrix) - vis[name + "/image_plane"].set_transform(matrix2) - - # set the transform of the camera - vis[name].set_transform(pose) - - -def set_persp_intrinsics_matrix(vis, K): - pp_w = K[0, 2] - pp_h = K[1, 2] - assert K[0, 0] == K[1, 1] - focal_length = K[0, 0] - x = pp_h / (focal_length) - fov = 2.0 * np.arctan(x) * (180.0 / np.pi) - vis["/Cameras/Main Camera/"].set_property("fov", fov) - vis["/Cameras/Main Camera/"].set_property("aspect", float(pp_w / pp_h)) # three.js expects width/height - - -def set_persp_pose(vis, pose, colmap=True): - pose_processed = copy.deepcopy(pose) - if colmap: - pose_processed[:, 1:3] *= -1 - vis["/Cameras/Main Camera/"].set_transform(pose_processed) - - -def set_persp_camera(vis, pose, K, colmap=True): - """Assumes simple pinhole model for intrinsics. - Args: - colmap: whether to use the colmap camera coordinate convention or not - """ - set_persp_intrinsics_matrix(vis, K) - set_persp_pose(vis, pose, colmap=colmap) - - -def set_camera(vis, camera: Camera): - pose = camera.get_camera_to_world_h() - K = camera.get_intrinsics_matrix() - set_persp_camera(vis, pose=pose.double().numpy(), K=K.double().numpy()) - - -def draw_aabb(vis, aabb, name="aabb"): - """Draw the axis-aligned bounding box.""" - lengths = aabb[1] - aabb[0] - - w = 1 - aaa = np.array([w, w, w]) - aab = np.array([w, w, -w]) - aba = np.array([w, -w, w]) - baa = np.array([-w, w, w]) - abb = np.array([w, -w, -w]) - bba = np.array([-w, -w, w]) - bab = np.array([-w, w, -w]) - bbb = np.array([-w, -w, -w]) - camera_points = [aaa, aab, aaa, aba, aab, abb, aba, abb] - camera_points.extend([baa, bab, baa, bba, bab, bbb, bba, bbb]) - camera_points.extend([aaa, baa, aab, bab, aba, bba, abb, bbb]) - lines = np.stack(camera_points) - - lines = lines * np.array(lengths) / 2.0 - - line_segments = g.LineSegments( - g.PointsGeometry(position=lines.astype(np.float32).T), - g.LineBasicMaterial(vertexColors=True), - ) - vis[name].set_object(line_segments) - center = aabb[0] + lengths / 2.0 - vis[name].set_transform(get_translation_matrix(center.tolist())) diff --git a/nerfactory/viewer/server/visualizer.py b/nerfactory/viewer/server/visualizer.py index 96d0b27c9..41b667de4 100644 --- a/nerfactory/viewer/server/visualizer.py +++ b/nerfactory/viewer/server/visualizer.py @@ -12,28 +12,25 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Code to connect and send commands to the viewer. +""" + import logging import signal import sys +from typing import Dict, Optional import msgpack import msgpack_numpy -import numpy as np import umsgpack import zmq -from nerfactory.viewer.server.commands import ( - Delete, - GetObject, - SetObject, - SetOutputOptions, - SetProperty, - SetTransform, -) from nerfactory.viewer.server.path import Path class ViewerWindow: + """The viewer window has the ZMQ connection to the viewer bridge server.""" + context = zmq.Context() def __init__(self, zmq_url): @@ -43,15 +40,15 @@ class ViewerWindow: self.assert_connected() def send(self, command): - cmd_data = command.lower() + """Sends a command to the viewer bridge server.""" self.client.send_multipart( [ - cmd_data["type"].encode("utf-8"), - cmd_data["path"].encode("utf-8"), - umsgpack.packb(cmd_data), + command["type"].encode("utf-8"), + command["path"].encode("utf-8"), + umsgpack.packb(command), ] ) - return self.client.recv() + return umsgpack.unpackb(self.client.recv()) def send_ping(self): """Tries to contact the viewer bridge server.""" @@ -65,7 +62,7 @@ class ViewerWindow: data, ] ) - return self.client.recv() + return umsgpack.unpackb(self.client.recv()) def assert_connected(self, timeout_in_sec: int = 5): """Check if the connection was established properly within some time. @@ -84,7 +81,7 @@ class ViewerWindow: _ = self.send_ping() logging.info("Successfully connected.") signal.alarm(0) # cancel the alarm - except Exception as e: + except Exception as e: # pylint: disable=broad-except logging.info(e) sys.exit() @@ -93,19 +90,18 @@ class Viewer: """Visualizer class for connecting to the bridge server. Args: - zmq_url (str, optional): _description_. Defaults to None. - window (ViewerWindow, optional): _description_. Defaults to None. - timeout (str, optional): _description_. Defaults to None. + zmq_url: Where to connect with ZMQ. Defaults to None. + window: An already existing ViewerWindow. Defaults to None. """ - def __init__(self, zmq_url: str = None, window: ViewerWindow = None): + def __init__(self, zmq_url: Optional[str] = None, window: Optional[ViewerWindow] = None): if zmq_url is None and window is None: raise ValueError("Must specify either zmq_url or window.") if window is None: self.window = ViewerWindow(zmq_url=zmq_url) else: self.window = window - self.path = Path(("nerfactory",)) + self.path = Path(()) @staticmethod def view_into(window: ViewerWindow, path: Path): @@ -117,28 +113,25 @@ class Viewer: def __getitem__(self, path): return Viewer.view_into(self.window, self.path.append(path)) - def set_object(self, geometry, material=None): - """Set the object at the current path - Args: - geometry (_type_): _description_ - material (_type_, optional): _description_. Defaults to None. + def __repr__(self): + return f"" - Returns: - _type_: _description_ - """ - return self.window.send(SetObject(geometry, material, self.path)) + def write(self, data: Optional[Dict] = None): + """Write data.""" + path = self.path.lower() + return self.window.send({"type": "write", "path": path, "data": data}) - def get_object(self): - """Get the object at the current path.""" - data = self.window.send(GetObject(self.path)) - data = umsgpack.unpackb(data) - if isinstance(data, str) and data.find("error") == 0: - # some error meaning that the object does not exist - return None - return data + def read(self): + """Read data.""" + path = self.path.lower() + return self.window.send({"type": "read", "path": path}) + + def delete(self): + """Delete data.""" + return self.write(data=None) def set_image(self, image): - """Set the image""" + """Sends an image to the viewer with WebRTC.""" type_ = "set_image" path = self.path.lower() data = msgpack.packb(image, default=msgpack_numpy.encode, use_bin_type=True) @@ -150,23 +143,3 @@ class Viewer: ] ) return self.window.client.recv() - - def set_transform(self, matrix=np.eye(4)): - """Set the transform""" - assert matrix.shape == (4, 4) - return self.window.send(SetTransform(matrix, self.path)) - - def set_property(self, key, value): - """Set the property""" - return self.window.send(SetProperty(key, value, self.path)) - - def set_output_options(self, options): - """Set the output options""" - return self.window.send(SetOutputOptions(options, self.path)) - - def delete(self): - """Delete the contents of the window""" - return self.window.send(Delete(self.path)) - - def __repr__(self): - return f"" diff --git a/notebooks/README.md b/notebooks/README.md index e69de29bb..923ecd7ca 100644 --- a/notebooks/README.md +++ b/notebooks/README.md @@ -0,0 +1 @@ +# Notebooks diff --git a/notebooks/notebook_imports.py b/notebooks/notebook_imports.py deleted file mode 100644 index 9165b4d67..000000000 --- a/notebooks/notebook_imports.py +++ /dev/null @@ -1,29 +0,0 @@ -# image stuff -import imageio -import mediapy as media - -# plotly stuff -import plotly.graph_objects as go - -# ipython stuff -from IPython import get_ipython - - -def setup_ipynb(): - """ - https://stackoverflow.com/questions/15411967/how-can-i-check-if-code-is-executed-in-the-ipython-notebook - https://stackoverflow.com/questions/35595766/matplotlib-line-magic-causes-syntaxerror-in-python-script - This gets reference to the InteractiveShell instance - """ - try: - from IPython import get_ipython - - get_ipython().run_line_magic("load_ext", "autoreload") - get_ipython().run_line_magic("autoreload", "2") - get_ipython().run_line_magic("matplotlib", "inline") - return True - except: - return False - - -setup_ipynb() diff --git a/notebooks/visualize_dataset.ipynb b/notebooks/visualize_dataset.ipynb deleted file mode 100644 index 4c69537fa..000000000 --- a/notebooks/visualize_dataset.ipynb +++ /dev/null @@ -1,10543 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "3c00934d-0f14-4c1a-a563-94d627b377e5", - "metadata": {}, - "source": [ - "# Notebook to visualize a dataset\n", - "\n", - "Sanity check to ensure data is properly read\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "b15dfdc1-5054-40bc-9631-4c7426eecfd9", - "metadata": {}, - "outputs": [], - "source": [ - "from notebook_imports import *\n", - "\n", - "import torch\n", - "\n", - "from nerfactoryctory.data.dataloader import TrainDataloader\n", - "from nerfactoryctory.data.utils import get_dataset_inputs_from_dataset_config\n", - "from nerfactoryctory.graphs.modules.ray_generator import RayGenerator\n", - "from nerfactoryctory.graphs.modules.scene_colliders import AABBBoxCollider\n", - "from nerfactoryctory.utils.plotly import get_line_segments_from_lines\n", - "from nerfactoryctory.utils.misc import instantiate_from_dict_config\n", - "\n", - "\n", - "from hydra import compose, initialize\n", - "from tqdm import tqdm\n", - "import random" - ] - }, - { - "cell_type": "markdown", - "id": "2cb2fd5f", - "metadata": {}, - "source": [ - "## Load dataset" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "7fe7478d", - "metadata": {}, - "outputs": [], - "source": [ - "CONFIG_NAME = \"graph_default.yaml\"\n", - "DEVICE = \"cpu\"\n", - "\n", - "with initialize(version_base=\"1.2\", config_path=\"../configs\"):\n", - " config = compose(config_name=CONFIG_NAME)\n", - "\n", - "dataset_inputs = get_dataset_inputs_from_dataset_config(**config.data.dataset_inputs_train, split=\"train\")\n", - "\n", - "# ImageDataset\n", - "image_dataset_train = instantiate_from_dict_config(config.data.image_dataset_train, **dataset_inputs.as_dict())\n", - "# ImageSampler\n", - "image_sampler_train = instantiate_from_dict_config(\n", - " config.data.dataloader_train.image_sampler, dataset=image_dataset_train, device=DEVICE\n", - ")\n", - "# PixelSampler\n", - "pixel_sampler_train = instantiate_from_dict_config(config.data.dataloader_train.pixel_sampler, keep_full_image=True)\n", - "\n", - "ray_generator = RayGenerator(dataset_inputs.intrinsics, dataset_inputs.camera_to_world)\n", - "\n", - "dataloader_train = TrainDataloader(image_sampler_train, pixel_sampler_train)\n", - "iter_dataloader_train = iter(dataloader_train)\n", - "\n", - "\n", - "def show_batch(batch):\n", - " # set the color of the sampled rays\n", - " c, y, x = [i.flatten() for i in torch.split(batch[\"local_indices\"], 1, dim=-1)]\n", - " batch[\"full_image\"][c, y, x] = 0.0\n", - " batch[\"full_image\"][c, y, x, 0] = 1.0\n", - "\n", - " # batch[\"image\"] is num_images, h, w, 3\n", - " images = torch.split(batch[\"full_image\"], 1, dim=0)\n", - " image_list = [image[0] for image in images]\n", - " image = torch.cat(image_list, dim=1) # cat along the width dimension\n", - "\n", - " # the pink pixels are rays\n", - " media.show_image((image * 255).to(torch.uint8))\n", - "\n", - "\n", - "def sample_and_show_batch():\n", - " ray_indices, batch = next(iter_dataloader_train)\n", - " show_batch(batch)\n", - " return batch" - ] - }, - { - "cell_type": "markdown", - "id": "39349ea0", - "metadata": {}, - "source": [ - "## Visualize Image\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "19b85061-a1b0-458b-9d00-96cde6f37dca", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Example image:\n" - ] - }, - { - "data": { - "text/html": [ - "
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Example Batch. Pink pixels denote the batch of rays:\n" - ] - }, - { - "data": { - "text/html": [ - "
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"Example image:\")\n", - "media.show_image(image_dataset_train.get_image(10), height=200)\n", - "print(\"Example Batch. Pink pixels denote the batch of rays:\")\n", - "batch = sample_and_show_batch()" - ] - }, - { - "cell_type": "markdown", - "id": "6eaab4db", - "metadata": {}, - "source": [ - "## Visualize cameras and ray\n", - "Camera origins denoted with black dots. A subset of camera rays are rendered for some cameras." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "fd8cf48a-823e-4395-99ca-eda4c2026440", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - " \n", - " " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "marker": { - "color": "rgba(0, 0, 0, 1)", - "size": 8 - }, - "mode": "markers", - "name": "origins", - "type": "scatter3d", - "x": [ - -0.05379832163453102, - -1.398659110069275, - -3.385493516921997, - -1.2464345693588257, - 2.8865230083465576, - -2.8906705379486084, - 2.232801914215088, - -0.12979382276535034, - -3.7495899200439453, - -3.4902923107147217, - -3.3551595211029053, - 0.52907395362854, - -0.48754292726516724, - 2.1348819732666016, - -1.9585098028182983, - 2.6538169384002686, - -3.1899499893188477, - 3.7242627143859863, - 0.9608983993530273, - 3.1680095195770264, - 1.9119539260864258, - -0.7987216114997864, - 2.8507351875305176, - -2.8561718463897705, - -2.1008365154266357, - 3.637409210205078, - 0.8770476579666138, - -0.42073559761047363, - 2.111776828765869, - -1.6116830110549927, - 1.7322875261306763, - -1.2971957921981812, - -3.9398677349090576, - 1.6561487913131714, - -2.247554063796997, - -3.3836936950683594, - 1.7566769123077393, - 0.8783321380615234, - -2.10075044631958, - -0.13960279524326324, - -3.3427348136901855, - -0.41710659861564636, - 0.47436219453811646, - 1.9496005773544312, - -1.8393374681472778, - -2.589273452758789, - 0.007521961349993944, - 0.42152610421180725, - 0.014908524230122566, - 1.434301733970642, - 1.1368069648742676, - -2.0656259059906006, - -3.002284049987793, - -3.886370897293091, - -3.3270153999328613, - 1.413723111152649, - -0.6229158639907837, - 0.339978963136673, - -2.315235137939453, - 3.168043851852417, - -2.7338404655456543, - -0.020868491381406784, - -1.1724741458892822, - 2.3183114528656006, - -0.8277195692062378, - 0.7858693599700928, - -1.683483362197876, - -0.019819926470518112, - 2.9369475841522217, - -3.872175693511963, - 1.0423099994659424, - -0.9127683639526367, - 3.5749754905700684, - 2.5896217823028564, - 2.546081066131592, - -0.7928509712219238, - -0.246016725897789, - 1.649032473564148, - -2.904823064804077, - 0.07294226437807083, - 2.52870512008667, - -1.7133750915527344, - -0.4118720591068268, - -3.530200481414795, - 0.692101001739502, - -3.933959484100342, - 1.1644508838653564, - -2.0406837463378906, - -0.00454852357506752, - -0.359662264585495, - -3.4366254806518555, - 0.21033930778503418, - -1.3956377506256104, - -1.8584816455841064, - -2.961758852005005, - 3.5639960765838623, - -3.008035659790039, - -3.4969570636749268, - -0.31046348810195923, - 2.1031012535095215 - ], - "y": [ - 3.845470428466797, - 3.5542497634887695, - -1.6727094650268555, - -1.6370664834976196, - 2.513216972351074, - 0.35454243421554565, - -1.9462224245071411, - -0.2752881646156311, - -0.607723593711853, - 0.8376882672309875, - -2.052626848220825, - 2.357914447784424, - 1.9911646842956543, - -3.3444597721099854, - 0.12458765506744385, - 2.5058655738830566, - -0.41941213607788086, - -1.1142032146453857, - 0.09413626790046692, - -2.1722869873046875, - -2.9874486923217773, - -1.6971794366836548, - 1.4779094457626343, - 1.5105533599853516, - -2.5676472187042236, - 0.5946717858314514, - 3.8690884113311768, - 2.9980766773223877, - -2.678037405014038, - -3.2011020183563232, - 0.5184860825538635, - 0.20353946089744568, - -0.0002467483573127538, - -1.8396297693252563, - 2.600733995437622, - 1.4670655727386475, - -2.0910537242889404, - 0.4593953788280487, - 3.081249952316284, - 1.6944570541381836, - 1.6285674571990967, - 3.9297399520874023, - 3.9500086307525635, - -1.4704318046569824, - -1.0502654314041138, - 1.7476580142974854, - -2.830711603164673, - 0.9678280353546143, - -1.1474518775939941, - -0.5783282518386841, - 2.938887357711792, - 3.2936851978302, - -2.0935845375061035, - -0.8260704278945923, - 1.048954725265503, - -3.6711270809173584, - 0.43254923820495605, - 1.7784502506256104, - 0.8177154660224915, - 0.929991602897644, - 2.8357691764831543, - 2.721303939819336, - 0.31767338514328003, - -3.021360397338867, - 3.885237693786621, - -3.873871326446533, - 1.7009721994400024, - 3.505112648010254, - -1.7953004837036133, - -0.925791323184967, - -3.6946234703063965, - -2.8198866844177246, - -1.238826036453247, - -2.1953184604644775, - 2.666229724884033, - 3.892266273498535, - -3.701932668685913, - 0.5256894826889038, - 2.6168203353881836, - 1.9539530277252197, - -1.077824592590332, - 3.422126293182373, - 1.2020422220230103, - 0.1713196337223053, - -2.587653875350952, - 0.5637863874435425, - -1.5312353372573853, - -1.0129939317703247, - 0.0694514811038971, - -3.9271185398101807, - -0.2910483777523041, - -1.1386629343032837, - -2.2291290760040283, - -1.0084656476974487, - 1.6074209213256836, - -1.813332438468933, - 1.805651307106018, - 0.29924672842025757, - -3.731215476989746, - -1.032314419746399 - ], - "z": [ - 1.2080823183059692, - 1.2888214588165283, - 1.4108426570892334, - 3.4664700031280518, - 1.26559317111969, - 2.7871713638305664, - 2.7343761920928955, - 4.019623279571533, - 1.3495359420776367, - 1.834703803062439, - 0.882965087890625, - 3.2265028953552246, - 3.4709601402282715, - 0.7119466662406921, - 3.521181106567383, - 1.7111091613769531, - 2.4286444187164307, - 1.066967248916626, - 3.913797616958618, - 1.2226545810699463, - 1.915615439414978, - 3.5681405067443848, - 2.437025308609009, - 2.4105000495910645, - 2.2899067401885986, - 1.6326723098754883, - 0.7148017883300781, - 2.6616756916046143, - 2.1490728855133057, - 1.8453792333602905, - 3.60282564163208, - 3.8112800121307373, - 0.8529018759727478, - 3.1816558837890625, - 2.1058690547943115, - 1.6273707151412964, - 2.965060234069824, - 3.9073634147644043, - 1.5306031703948975, - 3.655041217803955, - 1.5568854808807373, - 0.7957176566123962, - 0.6499325037002563, - 3.207318067550659, - 3.4298367500305176, - 2.5478134155273438, - 2.8700201511383057, - 3.8904531002044678, - 3.8643410205841064, - 3.7226758003234863, - 2.514082431793213, - 1.0652819871902466, - 1.6891403198242188, - 0.6809762716293335, - 2.0200648307800293, - 0.8798936009407043, - 3.959151029586792, - 3.6016008853912354, - 3.1970341205596924, - 2.31270694732666, - 0.8570468425750732, - 2.973897933959961, - 3.8437466621398926, - 1.3216705322265625, - 0.6854256391525269, - 0.7909042835235596, - 3.243852376937866, - 1.990927815437317, - 2.097911834716797, - 0.6317951679229736, - 1.2301819324493408, - 2.7322323322296143, - 1.3909926414489746, - 2.173576593399048, - 1.6305487155914307, - 0.6867690086364746, - 1.576442003250122, - 3.640651226043701, - 0.9819653034210205, - 3.5251591205596924, - 2.948549509048462, - 1.2662529945373535, - 3.8256313800811768, - 1.9386422634124756, - 3.012481451034546, - 0.6753571033477783, - 3.5425093173980713, - 3.325575590133667, - 4.0305280685424805, - 0.8356927633285522, - 2.086838722229004, - 3.861243963241577, - 3.055025339126587, - 3.432060956954956, - 2.212280035018921, - 0.5096641182899475, - 1.9852819442749023, - 1.982862114906311, - 1.4938690662384033, - 3.2804408073425293 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8914490938186646, - -0.16922688484191895, - 0.8226028680801392 - ], - [ - 0.325228214263916, - 1.3703604936599731, - 1.1156476736068726 - ] - ], - "y": [ - [ - 0.5180797576904297, - -0.6653734445571899, - 1.7256240844726562 - ], - [ - -1.0876350402832031, - 0.5340501070022583, - 0.7809998989105225 - ] - ], - "z": [ - [ - 1.573108196258545, - -0.6112486124038696, - 0.07401898503303528 - ], - [ - -0.2912161350250244, - 0.5144170522689819, - -0.333931565284729 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.7626197338104248, - -0.04258835315704346, - 0.8467072248458862 - ], - [ - -0.4419839382171631, - 2.1245265007019043, - 1.2591955661773682 - ] - ], - "y": [ - [ - 0.3860018253326416, - -0.5667150020599365, - 1.6479241847991943 - ], - [ - -1.8741936683654785, - 1.1215869188308716, - 0.31827640533447266 - ] - ], - "z": [ - [ - 1.4197585582733154, - -0.5186570882797241, - 0.04046306014060974 - ], - [ - -1.2044541835784912, - 1.0658236742019653, - -0.5337658524513245 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.1664161682128906, - -0.43951845169067383, - 0.7711556553840637 - ], - [ - -0.7262804508209229, - 2.4039883613586426, - 1.3123881816864014 - ] - ], - "y": [ - [ - 0.799980640411377, - -0.8759454488754272, - 1.891463279724121 - ], - [ - -2.165658950805664, - 1.3393033742904663, - 0.14681029319763184 - ] - ], - "z": [ - [ - 1.900410771369934, - -0.8088716268539429, - 0.14563918113708496 - ], - [ - -1.5428617000579834, - 1.2701517343521118, - -0.6078160405158997 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.0288596153259277, - -0.3043009042739868, - 0.7968928813934326 - ], - [ - -0.16124510765075684, - 1.848561406135559, - 1.2066683769226074 - ] - ], - "y": [ - [ - 0.6589553356170654, - -0.7706035375595093, - 1.8084996938705444 - ], - [ - -1.5863754749298096, - 0.9065951108932495, - 0.4875965118408203 - ] - ], - "z": [ - [ - 1.7366726398468018, - -0.7100076675415039, - 0.10981008410453796 - ], - [ - -0.870281457901001, - 0.864052414894104, - -0.46064233779907227 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.2506983280181885, - -0.5223672389984131, - 0.755386233329773 - ], - [ - -0.39108729362487793, - 2.074495315551758, - 1.2496726512908936 - ] - ], - "y": [ - [ - 0.8863879442214966, - -0.9404892921447754, - 1.942295789718628 - ], - [ - -1.8220133781433105, - 1.08260977268219, - 0.34897327423095703 - ] - ], - "z": [ - [ - 2.000734329223633, - -0.8694462776184082, - 0.16759195923805237 - ], - [ - -1.1438701152801514, - 1.0292433500289917, - -0.5205089449882507 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.5242877006530762, - 0.19169080257415771, - 0.8912999033927917 - ], - [ - 0.09078383445739746, - 1.6008182764053345, - 1.159512996673584 - ] - ], - "y": [ - [ - 0.14165997505187988, - -0.38419854640960693, - 1.5041805505752563 - ], - [ - -1.327991247177124, - 0.7135893106460571, - 0.6396009922027588 - ] - ], - "z": [ - [ - 1.136064052581787, - -0.3473644256591797, - -0.021614789962768555 - ], - [ - -0.5702831745147705, - 0.6829158067703247, - -0.39499688148498535 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.015348434448242, - -0.2910195589065552, - 0.7994208931922913 - ], - [ - -0.15828394889831543, - 1.845650553703308, - 1.2061142921447754 - ] - ], - "y": [ - [ - 0.6451035737991333, - -0.7602566480636597, - 1.8003507852554321 - ], - [ - -1.5833396911621094, - 0.9043272733688354, - 0.489382266998291 - ] - ], - "z": [ - [ - 1.7205898761749268, - -0.7002969980239868, - 0.10629087686538696 - ], - [ - -0.8667566776275635, - 0.8619242906570435, - -0.4598710536956787 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.0105090141296387, - -0.28626227378845215, - 0.800326406955719 - ], - [ - -0.20577621459960938, - 1.892335295677185, - 1.2150002717971802 - ] - ], - "y": [ - [ - 0.6401418447494507, - -0.7565504312515259, - 1.7974319458007812 - ], - [ - -1.6320295333862305, - 0.9406973123550415, - 0.4607386589050293 - ] - ], - "z": [ - [ - 1.7148292064666748, - -0.6968187093734741, - 0.10503029823303223 - ], - [ - -0.923288106918335, - 0.8960577249526978, - -0.47224128246307373 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8462870121002197, - -0.1248328685760498, - 0.831052839756012 - ], - [ - -0.22632932662963867, - 1.9125388860702515, - 1.2188458442687988 - ] - ], - "y": [ - [ - 0.47177886962890625, - -0.6307880878448486, - 1.6983858346939087 - ], - [ - -1.6531007289886475, - 0.9564369916915894, - 0.44834256172180176 - ] - ], - "z": [ - [ - 1.519350290298462, - -0.5787899494171143, - 0.06225574016571045 - ], - [ - -0.9477531909942627, - 0.9108294248580933, - -0.47759467363357544 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8648284673690796, - -0.1430588960647583, - 0.8275836706161499 - ], - [ - -0.29064393043518066, - 1.9757596254348755, - 1.230879306793213 - ] - ], - "y": [ - [ - 0.4907877445220947, - -0.6449871063232422, - 1.7095686197280884 - ], - [ - -1.7190370559692383, - 1.0056895017623901, - 0.40955305099487305 - ] - ], - "z": [ - [ - 1.5414206981658936, - -0.5921159982681274, - 0.0670851469039917 - ], - [ - -1.0243089199066162, - 0.9570533037185669, - -0.494346559047699 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 3.5639960765838623, - -1.813332438468933, - 0.5096641182899475 - ], - [ - 3.5639960765838623, - -1.813332438468933, - 0.5096641182899475 - ] - ], - "y": [ - [ - 2.232801914215088, - -1.9462224245071411, - 2.7343761920928955 - ], - [ - 2.232801914215088, - -1.9462224245071411, - 2.7343761920928955 - ] - ], - "z": [ - [ - 3.5639960765838623, - -1.813332438468933, - 0.5096641182899475 - ], - [ - 3.5639960765838623, - -1.813332438468933, - 0.5096641182899475 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.731647253036499, - -0.012142539024353027, - 0.8525023460388184 - ], - [ - -0.30777835845947266, - 1.9926027059555054, - 1.234085202217102 - ] - ], - "y": [ - [ - 0.3542482852935791, - -0.5429960489273071, - 1.6292439699172974 - ], - [ - -1.7366037368774414, - 1.0188113451004028, - 0.3992187976837158 - ] - ], - "z": [ - [ - 1.3828909397125244, - -0.49639666080474854, - 0.03239572048187256 - ], - [ - -1.0447046756744385, - 0.9693681001663208, - -0.4988095164299011 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.9034056663513184, - -0.18098020553588867, - 0.8203657865524292 - ], - [ - -0.37729597091674805, - 2.0609383583068848, - 1.2470921277999878 - ] - ], - "y": [ - [ - 0.5303378105163574, - -0.6745299100875854, - 1.7328354120254517 - ], - [ - -1.8078742027282715, - 1.072048306465149, - 0.35729122161865234 - ] - ], - "z": [ - [ - 1.5873404741287231, - -0.6198419332504272, - 0.07713329792022705 - ], - [ - -1.1274535655975342, - 1.0193313360214233, - -0.5169166922569275 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.853432536125183, - -0.1318567991256714, - 0.8297159075737 - ], - [ - 0.12034153938293457, - 1.5717631578445435, - 1.1539826393127441 - ] - ], - "y": [ - [ - 0.47910451889038086, - -0.6362601518630981, - 1.7026954889297485 - ], - [ - -1.2976882457733154, - 0.6909538507461548, - 0.6574277877807617 - ] - ], - "z": [ - [ - 1.5278558731079102, - -0.5839256048202515, - 0.06411689519882202 - ], - [ - -0.5350997447967529, - 0.6616722345352173, - -0.387298047542572 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.9850397109985352, - -0.2612260580062866, - 0.8050917387008667 - ], - [ - -0.23113346099853516, - 1.9172612428665161, - 1.2197446823120117 - ] - ], - "y": [ - [ - 0.6140303611755371, - -0.7370458841323853, - 1.7820708751678467 - ], - [ - -1.6580262184143066, - 0.9601160287857056, - 0.44544506072998047 - ] - ], - "z": [ - [ - 1.6845122575759888, - -0.6785135269165039, - 0.09839639067649841 - ], - [ - -0.9534718990325928, - 0.9142822027206421, - -0.47884601354599 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.9763017892837524, - -0.25263679027557373, - 0.8067266941070557 - ], - [ - -0.2521357536315918, - 1.9379063844680786, - 1.2236742973327637 - ] - ], - "y": [ - [ - 0.6050721406936646, - -0.7303543090820312, - 1.7768008708953857 - ], - [ - -1.6795580387115479, - 0.976199746131897, - 0.43277812004089355 - ] - ], - "z": [ - [ - 1.674111247062683, - -0.6722334623336792, - 0.09612041711807251 - ], - [ - -0.9784715175628662, - 0.9293769598007202, - -0.48431646823883057 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.4682552814483643, - 0.24677050113677979, - 0.90178382396698 - ], - [ - 0.08259201049804688, - 1.6088706254959106, - 1.1610456705093384 - ] - ], - "y": [ - [ - 0.08421444892883301, - -0.3412884473800659, - 1.470386028289795 - ], - [ - -1.3363895416259766, - 0.7198628187179565, - 0.6346602439880371 - ] - ], - "z": [ - [ - 1.069366455078125, - -0.3070930242538452, - -0.03620952367782593 - ], - [ - -0.5800340175628662, - 0.6888033151626587, - -0.39713054895401 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.7357730865478516, - -0.016198277473449707, - 0.8517303466796875 - ], - [ - -0.5793235301971436, - 2.259530544281006, - 1.284892201423645 - ] - ], - "y": [ - [ - 0.3584781885147095, - -0.5461556911468506, - 1.6317323446273804 - ], - [ - -2.0149965286254883, - 1.2267626523971558, - 0.2354435920715332 - ] - ], - "z": [ - [ - 1.3878018856048584, - -0.4993619918823242, - 0.033470362424850464 - ], - [ - -1.367933988571167, - 1.1645315885543823, - -0.569538414478302 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.5100915431976318, - 0.20564568042755127, - 0.8939560651779175 - ], - [ - -0.08186936378479004, - 1.7705353498458862, - 1.1918169260025024 - ] - ], - "y": [ - [ - 0.127105712890625, - -0.37332701683044434, - 1.495618462562561 - ], - [ - -1.5049982070922852, - 0.8458083868026733, - 0.5354697704315186 - ] - ], - "z": [ - [ - 1.1191656589508057, - -0.33716142177581787, - -0.025312483310699463 - ], - [ - -0.7757976055145264, - 0.8070040941238403, - -0.4399675130844116 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8520190715789795, - -0.13046741485595703, - 0.8299803733825684 - ], - [ - -0.3158743381500244, - 2.000561237335205, - 1.2355999946594238 - ] - ], - "y": [ - [ - 0.47765541076660156, - -0.6351776123046875, - 1.7018429040908813 - ], - [ - -1.744903802871704, - 1.0250111818313599, - 0.3943359851837158 - ] - ], - "z": [ - [ - 1.5261733531951904, - -0.5829097032546997, - 0.06374871730804443 - ], - [ - -1.0543415546417236, - 0.9751867055892944, - -0.5009183287620544 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.9472709894180298, - -0.2240995168685913, - 0.812158465385437 - ], - [ - -0.321913480758667, - 2.006497859954834, - 1.2367298603057861 - ] - ], - "y": [ - [ - 0.5753092765808105, - -0.7081222534179688, - 1.759291648864746 - ], - [ - -1.7510952949523926, - 1.029636025428772, - 0.39069366455078125 - ] - ], - "z": [ - [ - 1.6395548582077026, - -0.6513686180114746, - 0.08855882287025452 - ], - [ - -1.061530351638794, - 0.9795273542404175, - -0.502491295337677 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.6329998970031738, - 0.08482730388641357, - 0.8709595203399658 - ], - [ - 0.24136757850646973, - 1.452795147895813, - 1.131338357925415 - ] - ], - "y": [ - [ - 0.2531135082244873, - -0.46745121479034424, - 1.5697474479675293 - ], - [ - -1.1736104488372803, - 0.5982712507247925, - 0.7304215431213379 - ] - ], - "z": [ - [ - 1.265467643737793, - -0.4254974126815796, - 0.006701231002807617 - ], - [ - -0.391038179397583, - 0.5746890306472778, - -0.35577458143234253 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8247497081756592, - -0.10366177558898926, - 0.8350825309753418 - ], - [ - -0.7032296657562256, - 2.3813295364379883, - 1.3080754280090332 - ] - ], - "y": [ - [ - 0.44969844818115234, - -0.6142945289611816, - 1.6853961944580078 - ], - [ - -2.142026901245117, - 1.3216508626937866, - 0.16071295738220215 - ] - ], - "z": [ - [ - 1.4937138557434082, - -0.5633107423782349, - 0.05664592981338501 - ], - [ - -1.5154235363006592, - 1.2535847425460815, - -0.6018120646476746 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8011661767959595, - -0.08047926425933838, - 0.839495062828064 - ], - [ - -0.40949487686157227, - 2.092589855194092, - 1.2531166076660156 - ] - ], - "y": [ - [ - 0.42552030086517334, - -0.5962342023849487, - 1.6711723804473877 - ], - [ - -1.8408851623535156, - 1.096706509590149, - 0.3378713130950928 - ] - ], - "z": [ - [ - 1.4656414985656738, - -0.546360969543457, - 0.050503164529800415 - ], - [ - -1.1657812595367432, - 1.0424731969833374, - -0.5253034234046936 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.068432331085205, - -0.3432004451751709, - 0.7894887924194336 - ], - [ - 0.1305065155029297, - 1.561771035194397, - 1.152080774307251 - ] - ], - "y": [ - [ - 0.6995255947113037, - -0.8009084463119507, - 1.832366704940796 - ], - [ - -1.2872672080993652, - 0.6831694841384888, - 0.6635584831237793 - ] - ], - "z": [ - [ - 1.7837769985198975, - -0.7384488582611084, - 0.1201174259185791 - ], - [ - -0.5230000019073486, - 0.6543663740158081, - -0.38465040922164917 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8136311769485474, - -0.09273231029510498, - 0.8371628522872925 - ], - [ - -0.73050856590271, - 2.408144474029541, - 1.31317937374115 - ] - ], - "y": [ - [ - 0.43829965591430664, - -0.6057800054550171, - 1.6786903142929077 - ], - [ - -2.1699938774108887, - 1.3425413370132446, - 0.14426040649414062 - ] - ], - "z": [ - [ - 1.4804790019989014, - -0.5553197860717773, - 0.053749918937683105 - ], - [ - -1.5478947162628174, - 1.2731906175613403, - -0.6089172959327698 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.538773775100708, - 0.1774512529373169, - 0.8885895609855652 - ], - [ - 0.07942509651184082, - 1.6119838953018188, - 1.1616382598876953 - ] - ], - "y": [ - [ - 0.1565113067626953, - -0.3952920436859131, - 1.512917399406433 - ], - [ - -1.3396365642547607, - 0.7222880125045776, - 0.6327502727508545 - ] - ], - "z": [ - [ - 1.1533069610595703, - -0.3577756881713867, - -0.017841696739196777 - ], - [ - -0.5838038921356201, - 0.6910794973373413, - -0.3979554772377014 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.6909633874893188, - 0.027849555015563965, - 0.8601143956184387 - ], - [ - 0.11019349098205566, - 1.5817385911941528, - 1.155881404876709 - ] - ], - "y": [ - [ - 0.3125385046005249, - -0.5118399858474731, - 1.6047065258026123 - ], - [ - -1.3080923557281494, - 0.6987253427505493, - 0.6513073444366455 - ] - ], - "z": [ - [ - 1.334463357925415, - -0.46715664863586426, - 0.02179884910583496 - ], - [ - -0.5471789836883545, - 0.6689656972885132, - -0.3899412751197815 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.5645673274993896, - 0.15209627151489258, - 0.8837634921073914 - ], - [ - 0.17816853523254395, - 1.514919400215149, - 1.1431629657745361 - ] - ], - "y": [ - [ - 0.18295526504516602, - -0.41504502296447754, - 1.5284740924835205 - ], - [ - -1.238403081893921, - 0.6466695070266724, - 0.6923048496246338 - ] - ], - "z": [ - [ - 1.1840100288391113, - -0.3763139247894287, - -0.011123239994049072 - ], - [ - -0.4662659168243408, - 0.6201108694076538, - -0.37223589420318604 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.788474440574646, - -0.06800341606140137, - 0.8418697714805603 - ], - [ - -0.7922303676605225, - 2.4688167572021484, - 1.3247277736663818 - ] - ], - "y": [ - [ - 0.412508487701416, - -0.5865147113800049, - 1.663517713546753 - ], - [ - -2.233272075653076, - 1.3898085355758667, - 0.10703444480895996 - ] - ], - "z": [ - [ - 1.4505341053009033, - -0.5372391939163208, - 0.04719740152359009 - ], - [ - -1.6213643550872803, - 1.3175510168075562, - -0.6249939799308777 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8873653411865234, - -0.16521263122558594, - 0.8233669996261597 - ], - [ - -0.4659006595611572, - 2.148036479949951, - 1.2636704444885254 - ] - ], - "y": [ - [ - 0.5138930082321167, - -0.6622461080551147, - 1.7231611013412476 - ], - [ - -1.8987135887145996, - 1.1399027109146118, - 0.3038513660430908 - ] - ], - "z": [ - [ - 1.5682471990585327, - -0.6083135604858398, - 0.07295531034469604 - ], - [ - -1.2329232692718506, - 1.0830129384994507, - -0.5399954915046692 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.094330310821533, - -0.36865806579589844, - 0.7846431732177734 - ], - [ - -0.8405330181121826, - 2.5162978172302246, - 1.3337652683258057 - ] - ], - "y": [ - [ - 0.7260767221450806, - -0.8207412958145142, - 1.8479864597320557 - ], - [ - -2.282792091369629, - 1.426798701286316, - 0.07790207862854004 - ] - ], - "z": [ - [ - 1.8146042823791504, - -0.7570621967315674, - 0.126863032579422 - ], - [ - -1.6788599491119385, - 1.3522664308547974, - -0.637575089931488 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8441582918167114, - -0.12274038791656494, - 0.8314511775970459 - ], - [ - -0.33368897438049316, - 2.0180726051330566, - 1.2389330863952637 - ] - ], - "y": [ - [ - 0.4695965051651001, - -0.6291579008102417, - 1.6971019506454468 - ], - [ - -1.7631676197052002, - 1.0386537313461304, - 0.3835916519165039 - ] - ], - "z": [ - [ - 1.5168163776397705, - -0.5772600173950195, - 0.06170126795768738 - ], - [ - -1.0755469799041748, - 0.9879904985427856, - -0.505558431148529 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.6674540042877197, - 0.050959110260009766, - 0.8645130395889282 - ], - [ - 0.06827926635742188, - 1.622940182685852, - 1.1637237071990967 - ] - ], - "y": [ - [ - 0.28843629360198975, - -0.4938364028930664, - 1.5905274152755737 - ], - [ - -1.3510634899139404, - 0.7308236360549927, - 0.6260278224945068 - ] - ], - "z": [ - [ - 1.3064794540405273, - -0.4502600431442261, - 0.01567542552947998 - ], - [ - -0.5970709323883057, - 0.6990901231765747, - -0.4008585810661316 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.5758793354034424, - 0.1409766674041748, - 0.881646990776062 - ], - [ - 0.15319037437438965, - 1.5394729375839233, - 1.147836446762085 - ] - ], - "y": [ - [ - 0.19455242156982422, - -0.4237077236175537, - 1.5352966785430908 - ], - [ - -1.2640111446380615, - 0.6657980680465698, - 0.6772396564483643 - ] - ], - "z": [ - [ - 1.1974751949310303, - -0.384443998336792, - -0.008176863193511963 - ], - [ - -0.4959986209869385, - 0.6380633115768433, - -0.378741979598999 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.1714982986450195, - -0.4445139169692993, - 0.7702047824859619 - ], - [ - -0.6092627048492432, - 2.2889604568481445, - 1.2904939651489258 - ] - ], - "y": [ - [ - 0.8051906824111938, - -0.8798372745513916, - 1.8945283889770508 - ], - [ - -2.0456905364990234, - 1.2496904134750366, - 0.21738648414611816 - ] - ], - "z": [ - [ - 1.9064600467681885, - -0.8125240802764893, - 0.14696288108825684 - ], - [ - -1.4035718441009521, - 1.1860493421554565, - -0.5773367285728455 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.609310507774353, - 0.10811388492584229, - 0.8753919005393982 - ], - [ - 0.0680534839630127, - 1.6231621503829956, - 1.1637659072875977 - ] - ], - "y": [ - [ - 0.22882676124572754, - -0.44930970668792725, - 1.5554598569869995 - ], - [ - -1.351294994354248, - 0.7309964895248413, - 0.6258916854858398 - ] - ], - "z": [ - [ - 1.237269401550293, - -0.40847158432006836, - 0.0005308985710144043 - ], - [ - -0.5973398685455322, - 0.6992522478103638, - -0.4009174108505249 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.9631211757659912, - -0.23968029022216797, - 0.8091927766799927 - ], - [ - -0.2838163375854492, - 1.9690481424331665, - 1.2296018600463867 - ] - ], - "y": [ - [ - 0.5915591716766357, - -0.720260500907898, - 1.7688512802124023 - ], - [ - -1.7120373249053955, - 1.0004609823226929, - 0.41367101669311523 - ] - ], - "z": [ - [ - 1.6584218740463257, - -0.6627603769302368, - 0.09268727898597717 - ], - [ - -1.0161817073822021, - 0.9521461725234985, - -0.4925681948661804 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.7490936517715454, - -0.02929222583770752, - 0.8492380380630493 - ], - [ - -0.5497772693634033, - 2.2304863929748535, - 1.2793638706207275 - ] - ], - "y": [ - [ - 0.3721346855163574, - -0.55635666847229, - 1.6397662162780762 - ], - [ - -1.9847044944763184, - 1.204135775566101, - 0.2532637119293213 - ] - ], - "z": [ - [ - 1.4036579132080078, - -0.5089356899261475, - 0.03693994879722595 - ], - [ - -1.332763910293579, - 1.1432961225509644, - -0.5618425011634827 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.1078786849975586, - -0.38197600841522217, - 0.7821082472801208 - ], - [ - -0.8969829082489014, - 2.5717883110046387, - 1.3443272113800049 - ] - ], - "y": [ - [ - 0.7399667501449585, - -0.831116795539856, - 1.8561577796936035 - ], - [ - -2.340665817260742, - 1.4700287580490112, - 0.04385566711425781 - ] - ], - "z": [ - [ - 1.8307312726974487, - -0.7667995691299438, - 0.1303919553756714 - ], - [ - -1.7460544109344482, - 1.3928381204605103, - -0.6522786021232605 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.550339698791504, - 0.16608202457427979, - 0.8864254951477051 - ], - [ - 0.2329573631286621, - 1.4610623121261597, - 1.1329119205474854 - ] - ], - "y": [ - [ - 0.16836881637573242, - -0.40414929389953613, - 1.5198930501937866 - ], - [ - -1.1822326183319092, - 0.6047118902206421, - 0.7253491878509521 - ] - ], - "z": [ - [ - 1.16707444190979, - -0.36608827114105225, - -0.014829158782958984 - ], - [ - -0.4010491371154785, - 0.5807334184646606, - -0.3579651713371277 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.1631622314453125, - -0.43631982803344727, - 0.7717645168304443 - ], - [ - -0.062081098556518555, - 1.7510837316513062, - 1.1881145238876343 - ] - ], - "y": [ - [ - 0.7966445684432983, - -0.8734534978866577, - 1.8895007371902466 - ], - [ - -1.484710931777954, - 0.830654501914978, - 0.5474045276641846 - ] - ], - "z": [ - [ - 1.8965375423431396, - -0.8065328598022461, - 0.1447916328907013 - ], - [ - -0.7522432804107666, - 0.7927819490432739, - -0.43481332063674927 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.933075189590454, - -0.2101452350616455, - 0.8148145079612732 - ], - [ - -0.1412220001220703, - 1.8288787603378296, - 1.202921986579895 - ] - ], - "y": [ - [ - 0.5607556104660034, - -0.6972510814666748, - 1.7507297992706299 - ], - [ - -1.565847396850586, - 0.8912612199783325, - 0.49967288970947266 - ] - ], - "z": [ - [ - 1.6226571798324585, - -0.6411659717559814, - 0.08486127853393555 - ], - [ - -0.846447229385376, - 0.8496617078781128, - -0.45542699098587036 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 0.6749060153961182, - 1.026629090309143, - 1.0502219200134277 - ], - [ - -0.11934733390808105, - 1.8073762655258179, - 1.198829174041748 - ] - ], - "y": [ - [ - -0.7291398048400879, - 0.26626431941986084, - 0.9918986558914185 - ], - [ - -1.5434212684631348, - 0.8745094537734985, - 0.5128660202026367 - ] - ], - "z": [ - [ - 0.1250169277191162, - 0.2630985975265503, - -0.24285167455673218 - ], - [ - -0.8204090595245361, - 0.8339401483535767, - -0.449729323387146 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.810802936553955, - -0.08995211124420166, - 0.8376920223236084 - ], - [ - -0.3877732753753662, - 2.071237564086914, - 1.2490525245666504 - ] - ], - "y": [ - [ - 0.43540000915527344, - -0.6036139726638794, - 1.6769845485687256 - ], - [ - -1.8186159133911133, - 1.080072045326233, - 0.35097193717956543 - ] - ], - "z": [ - [ - 1.4771122932434082, - -0.5532870292663574, - 0.053013235330581665 - ], - [ - -1.139925241470337, - 1.026861548423767, - -0.519645631313324 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8490314483642578, - -0.12753057479858398, - 0.830539345741272 - ], - [ - -0.4222686290740967, - 2.1051464080810547, - 1.2555067539215088 - ] - ], - "y": [ - [ - 0.47459256649017334, - -0.6328897476196289, - 1.7000410556793213 - ], - [ - -1.8539810180664062, - 1.106488823890686, - 0.3301670551300049 - ] - ], - "z": [ - [ - 1.5226171016693115, - -0.5807623863220215, - 0.06297054886817932 - ], - [ - -1.1809861660003662, - 1.0516539812088013, - -0.5286306738853455 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.696293592453003, - 0.022609949111938477, - 0.8591170907020569 - ], - [ - 0.02638721466064453, - 1.6641196012496948, - 1.1715617179870605 - ] - ], - "y": [ - [ - 0.31800317764282227, - -0.515921950340271, - 1.6079212427139282 - ], - [ - -1.3940117359161377, - 0.7629047632217407, - 0.600761890411377 - ] - ], - "z": [ - [ - 1.3408081531524658, - -0.4709874391555786, - 0.023187190294265747 - ], - [ - -0.6469361782073975, - 0.7291983366012573, - -0.4117701053619385 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.958202838897705, - -0.23484551906585693, - 0.8101130723953247 - ], - [ - -0.37580370903015137, - 2.059471607208252, - 1.2468129396438599 - ] - ], - "y": [ - [ - 0.5865168571472168, - -0.7164939641952515, - 1.7658848762512207 - ], - [ - -1.8063445091247559, - 1.0709055662155151, - 0.35819125175476074 - ] - ], - "z": [ - [ - 1.6525673866271973, - -0.6592254638671875, - 0.09140622615814209 - ], - [ - -1.1256773471832275, - 1.0182589292526245, - -0.5165279507637024 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.2353618144989014, - 0.4757035970687866, - 0.9453588724136353 - ], - [ - -0.11093592643737793, - 1.7991076707839966, - 1.1972553730010986 - ] - ], - "y": [ - [ - -0.15455150604248047, - -0.16293704509735107, - 1.3299226760864258 - ], - [ - -1.5347976684570312, - 0.8680678606033325, - 0.5179390907287598 - ] - ], - "z": [ - [ - 0.7921459674835205, - -0.1397092342376709, - -0.0968707799911499 - ], - [ - -0.8103969097137451, - 0.8278945684432983, - -0.4475383758544922 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.6495087146759033, - 0.06859922409057617, - 0.8678706884384155 - ], - [ - 0.15092682838439941, - 1.5416978597640991, - 1.1482599973678589 - ] - ], - "y": [ - [ - 0.2700386047363281, - -0.48009371757507324, - 1.5797042846679688 - ], - [ - -1.266331672668457, - 0.6675313711166382, - 0.6758747100830078 - ] - ], - "z": [ - [ - 1.285118579864502, - -0.43736255168914795, - 0.011001259088516235 - ], - [ - -0.49869275093078613, - 0.6396900415420532, - -0.37933146953582764 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8308876752853394, - -0.10969531536102295, - 0.8339340686798096 - ], - [ - -0.6416547298431396, - 2.3208017349243164, - 1.2965545654296875 - ] - ], - "y": [ - [ - 0.455991268157959, - -0.6189950704574585, - 1.6890981197357178 - ], - [ - -2.0788989067077637, - 1.2744961977005005, - 0.19785022735595703 - ] - ], - "z": [ - [ - 1.5010199546813965, - -0.5677222013473511, - 0.058244675397872925 - ], - [ - -1.4421288967132568, - 1.2093299627304077, - -0.5857736468315125 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.6496641635894775, - 0.06844651699066162, - 0.8678416013717651 - ], - [ - -0.37554407119750977, - 2.0592164993286133, - 1.2467644214630127 - ] - ], - "y": [ - [ - 0.27019786834716797, - -0.4802126884460449, - 1.5797979831695557 - ], - [ - -1.8060784339904785, - 1.0707067251205444, - 0.35834765434265137 - ] - ], - "z": [ - [ - 1.2853035926818848, - -0.43747425079345703, - 0.011041730642318726 - ], - [ - -1.125368356704712, - 1.018072247505188, - -0.5164603590965271 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.7954237461090088, - -0.07483446598052979, - 0.8405694961547852 - ], - [ - -0.422438383102417, - 2.105313301086426, - 1.2555384635925293 - ] - ], - "y": [ - [ - 0.41963303089141846, - -0.5918365716934204, - 1.6677089929580688 - ], - [ - -1.8541550636291504, - 1.1066187620162964, - 0.3300645351409912 - ] - ], - "z": [ - [ - 1.458806037902832, - -0.5422338247299194, - 0.04900744557380676 - ], - [ - -1.1811883449554443, - 1.0517760515213013, - -0.5286749005317688 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.7135498523712158, - 0.005647182464599609, - 0.8558883666992188 - ], - [ - -0.18227505683898926, - 1.8692337274551392, - 1.21060311794281 - ] - ], - "y": [ - [ - 0.3356945514678955, - -0.5291368961334229, - 1.6183289289474487 - ], - [ - -1.6079356670379639, - 0.9226998090744019, - 0.4749128818511963 - ] - ], - "z": [ - [ - 1.3613488674163818, - -0.48338985443115234, - 0.02768191695213318 - ], - [ - -0.8953139781951904, - 0.8791669607162476, - -0.4661199450492859 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.049428939819336, - -0.324520468711853, - 0.7930443286895752 - ], - [ - -0.3144099712371826, - 1.9991217851638794, - 1.2353260517120361 - ] - ], - "y": [ - [ - 0.6800433397293091, - -0.78635573387146, - 1.8209054470062256 - ], - [ - -1.7434027194976807, - 1.0238898992538452, - 0.395219087600708 - ] - ], - "z": [ - [ - 1.7611569166183472, - -0.7247910499572754, - 0.11516773700714111 - ], - [ - -1.0525987148284912, - 0.9741343259811401, - -0.5005369782447815 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.7694603204727173, - -0.049312591552734375, - 0.845427393913269 - ], - [ - -0.5029776096343994, - 2.184483051300049, - 1.2706077098846436 - ] - ], - "y": [ - [ - 0.39301490783691406, - -0.5719536542892456, - 1.6520498991012573 - ], - [ - -1.93672513961792, - 1.168296456336975, - 0.28148937225341797 - ] - ], - "z": [ - [ - 1.427901029586792, - -0.5235735177993774, - 0.04224482178688049 - ], - [ - -1.2770569324493408, - 1.1096607446670532, - -0.5496527552604675 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.5347464084625244, - 0.1814100742340088, - 0.8893430829048157 - ], - [ - 0.11934757232666016, - 1.5727401971817017, - 1.1541686058044434 - ] - ], - "y": [ - [ - 0.15238237380981445, - -0.3922078609466553, - 1.5104883909225464 - ], - [ - -1.2987074851989746, - 0.6917151212692261, - 0.6568284034729004 - ] - ], - "z": [ - [ - 1.1485133171081543, - -0.3548811674118042, - -0.018890678882598877 - ], - [ - -0.5362827777862549, - 0.6623865365982056, - -0.38755691051483154 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.4473490715026855, - 0.26732122898101807, - 0.9056954383850098 - ], - [ - -0.33344411849975586, - 2.0178322792053223, - 1.2388873100280762 - ] - ], - "y": [ - [ - 0.06278109550476074, - -0.32527828216552734, - 1.4577770233154297 - ], - [ - -1.7629168033599854, - 1.0384663343429565, - 0.3837392330169678 - ] - ], - "z": [ - [ - 1.0444812774658203, - -0.29206740856170654, - -0.041654884815216064 - ], - [ - -1.0752556324005127, - 0.9878145456314087, - -0.5054946541786194 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.0014400482177734, - -0.2773476839065552, - 0.80202317237854 - ], - [ - -0.19171380996704102, - 1.8785120248794556, - 1.2123692035675049 - ] - ], - "y": [ - [ - 0.6308444738388062, - -0.7496055364608765, - 1.7919623851776123 - ], - [ - -1.617612600326538, - 0.929928183555603, - 0.4692199230194092 - ] - ], - "z": [ - [ - 1.7040343284606934, - -0.6903008222579956, - 0.1026681661605835 - ], - [ - -0.9065492153167725, - 0.8859509229660034, - -0.4685784578323364 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.015618085861206, - -0.291284441947937, - 0.7993704676628113 - ], - [ - -0.3905315399169922, - 2.073948860168457, - 1.2495685815811157 - ] - ], - "y": [ - [ - 0.6453797817230225, - -0.7604629993438721, - 1.8005132675170898 - ], - [ - -1.8214435577392578, - 1.0821841955184937, - 0.34930849075317383 - ] - ], - "z": [ - [ - 1.720910668373108, - -0.7004907131195068, - 0.10636106133460999 - ], - [ - -1.1432082653045654, - 1.0288439989089966, - -0.5203641057014465 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8531972169876099, - -0.13162553310394287, - 0.829759955406189 - ], - [ - 0.29412150382995605, - 1.4009383916854858, - 1.1214678287506104 - ] - ], - "y": [ - [ - 0.47886335849761963, - -0.6360799074172974, - 1.7025535106658936 - ], - [ - -1.1195263862609863, - 0.55787193775177, - 0.762238621711731 - ] - ], - "z": [ - [ - 1.5275757312774658, - -0.5837564468383789, - 0.06405562162399292 - ], - [ - -0.3282434940338135, - 0.5367740392684937, - -0.34203386306762695 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.7194703817367554, - -0.00017273426055908203, - 0.8547806739807129 - ], - [ - -0.34438562393188477, - 2.028587818145752, - 1.2409346103668213 - ] - ], - "y": [ - [ - 0.3417644500732422, - -0.5336709022521973, - 1.6218997240066528 - ], - [ - -1.7741341590881348, - 1.046845555305481, - 0.3771400451660156 - ] - ], - "z": [ - [ - 1.368396282196045, - -0.4876450300216675, - 0.029224038124084473 - ], - [ - -1.0882794857025146, - 0.9956783056259155, - -0.5083445906639099 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.7092114686965942, - 0.009911775588989258, - 0.8567001223564148 - ], - [ - 0.45377063751220703, - 1.2440041303634644, - 1.091597080230713 - ] - ], - "y": [ - [ - 0.3312467336654663, - -0.5258145332336426, - 1.6157124042510986 - ], - [ - -0.9558515548706055, - 0.435611367225647, - 0.8585267066955566 - ] - ], - "z": [ - [ - 1.356184720993042, - -0.4802718162536621, - 0.026551902294158936 - ], - [ - -0.13820791244506836, - 0.4220317602157593, - -0.3004503846168518 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.9878884553909302, - -0.264026403427124, - 0.8045587539672852 - ], - [ - -0.22428655624389648, - 1.910530924797058, - 1.218463659286499 - ] - ], - "y": [ - [ - 0.6169509887695312, - -0.7392274141311646, - 1.7837889194488525 - ], - [ - -1.6510066986083984, - 0.954872727394104, - 0.44957470893859863 - ] - ], - "z": [ - [ - 1.6879031658172607, - -0.6805609464645386, - 0.09913837909698486 - ], - [ - -0.945321798324585, - 0.9093614816665649, - -0.4770626425743103 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8734091520309448, - -0.15149366855621338, - 0.8259782195091248 - ], - [ - -0.2673976421356201, - 1.9529088735580444, - 1.226529836654663 - ] - ], - "y": [ - [ - 0.4995849132537842, - -0.6515582799911499, - 1.7147438526153564 - ], - [ - -1.695204734802246, - 0.9878875017166138, - 0.42357325553894043 - ] - ], - "z": [ - [ - 1.5516345500946045, - -0.598283052444458, - 0.06932014226913452 - ], - [ - -0.9966385364532471, - 0.9403458833694458, - -0.48829174041748047 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.6583393812179565, - 0.059918761253356934, - 0.8662184476852417 - ], - [ - -0.4613974094390869, - 2.1436100006103516, - 1.2628278732299805 - ] - ], - "y": [ - [ - 0.2790919542312622, - -0.4868563413619995, - 1.585030198097229 - ], - [ - -1.894096851348877, - 1.1364539861679077, - 0.3065674304962158 - ] - ], - "z": [ - [ - 1.2956299781799316, - -0.44370925426483154, - 0.013301342725753784 - ], - [ - -1.227562665939331, - 1.079776406288147, - -0.5388224720954895 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8804038763046265, - -0.1583695411682129, - 0.8246694803237915 - ], - [ - -0.25154542922973633, - 1.9373263120651245, - 1.2235639095306396 - ] - ], - "y": [ - [ - 0.506756067276001, - -0.6569149494171143, - 1.718962550163269 - ], - [ - -1.678952932357788, - 0.9757477045059204, - 0.4331340789794922 - ] - ], - "z": [ - [ - 1.5599608421325684, - -0.603310227394104, - 0.07114207744598389 - ], - [ - -0.977769136428833, - 0.9289528131484985, - -0.48416268825531006 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.0717787742614746, - -0.3464900255203247, - 0.7888626456260681 - ], - [ - -0.09714627265930176, - 1.7855523824691772, - 1.1946752071380615 - ] - ], - "y": [ - [ - 0.7029565572738647, - -0.8034712076187134, - 1.8343851566314697 - ], - [ - -1.520660161972046, - 0.857507586479187, - 0.5262560844421387 - ] - ], - "z": [ - [ - 1.7877604961395264, - -0.7408541440963745, - 0.1209891140460968 - ], - [ - -0.7939822673797607, - 0.8179837465286255, - -0.44394659996032715 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.6802705526351929, - 0.03836047649383545, - 0.8621150255203247 - ], - [ - 0.013767242431640625, - 1.6765252351760864, - 1.1739230155944824 - ] - ], - "y": [ - [ - 0.3015761375427246, - -0.5036513805389404, - 1.5982574224472046 - ], - [ - -1.4069499969482422, - 0.7725692987442017, - 0.5931503772735596 - ] - ], - "z": [ - [ - 1.3217353820800781, - -0.45947158336639404, - 0.01901373267173767 - ], - [ - -0.6619584560394287, - 0.7382687330245972, - -0.4150572419166565 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.007600784301758, - -0.2834036350250244, - 0.8008705377578735 - ], - [ - -0.5622079372406006, - 2.242705821990967, - 1.2816898822784424 - ] - ], - "y": [ - [ - 0.6371605396270752, - -0.7543234825134277, - 1.7956780195236206 - ], - [ - -1.9974493980407715, - 1.2136555910110474, - 0.24576640129089355 - ] - ], - "z": [ - [ - 1.7113676071166992, - -0.6947286128997803, - 0.10427284240722656 - ], - [ - -1.3475606441497803, - 1.1522303819656372, - -0.565080463886261 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 0.9930779933929443, - 0.7138673067092896, - 0.9906909465789795 - ], - [ - -0.12218379974365234, - 1.8101643323898315, - 1.1993598937988281 - ] - ], - "y": [ - [ - -0.40294480323791504, - 0.0226057767868042, - 1.183795690536499 - ], - [ - -1.5463292598724365, - 0.8766816854476929, - 0.5111551284790039 - ] - ], - "z": [ - [ - 0.5037474632263184, - 0.03442370891571045, - -0.1599779725074768 - ], - [ - -0.8237855434417725, - 0.835978627204895, - -0.45046812295913696 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8697110414505005, - -0.14785850048065186, - 0.8266701698303223 - ], - [ - -0.2549920082092285, - 1.9407142400741577, - 1.2242087125778198 - ] - ], - "y": [ - [ - 0.4957934617996216, - -0.648726224899292, - 1.7125133275985718 - ], - [ - -1.6824862957000732, - 0.9783869981765747, - 0.43105554580688477 - ] - ], - "z": [ - [ - 1.5472326278686523, - -0.5956251621246338, - 0.06835690140724182 - ], - [ - -0.9818713665008545, - 0.9314297437667847, - -0.4850603938102722 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.654796838760376, - 0.06340110301971436, - 0.866881251335144 - ], - [ - -0.4169642925262451, - 2.0999321937561035, - 1.254514217376709 - ] - ], - "y": [ - [ - 0.27546000480651855, - -0.48414337635040283, - 1.5828936100006104 - ], - [ - -1.8485426902770996, - 1.1024266481399536, - 0.33336639404296875 - ] - ], - "z": [ - [ - 1.2914133071899414, - -0.44116318225860596, - 0.01237863302230835 - ], - [ - -1.1746723651885986, - 1.0478416681289673, - -0.5272490382194519 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8844456672668457, - -0.16234266757965088, - 0.8239132165908813 - ], - [ - -0.2423853874206543, - 1.9283219575881958, - 1.2218499183654785 - ] - ], - "y": [ - [ - 0.5108997821807861, - -0.6600102186203003, - 1.721400260925293 - ], - [ - -1.6695618629455566, - 0.9687329530715942, - 0.4386589527130127 - ] - ], - "z": [ - [ - 1.5647717714309692, - -0.6062151193618774, - 0.07219481468200684 - ], - [ - -0.9668653011322021, - 0.922369122505188, - -0.48177677392959595 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.6524039506912231, - 0.06575322151184082, - 0.8673290014266968 - ], - [ - -0.05803370475769043, - 1.7471050024032593, - 1.1873571872711182 - ] - ], - "y": [ - [ - 0.273006796836853, - -0.4823108911514282, - 1.5814504623413086 - ], - [ - -1.4805614948272705, - 0.8275550603866577, - 0.5498456954956055 - ] - ], - "z": [ - [ - 1.288564920425415, - -0.4394434690475464, - 0.011755377054214478 - ], - [ - -0.7474253177642822, - 0.7898730039596558, - -0.43375903367996216 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.3321285247802734, - -0.6024128198623657, - 0.7401503324508667 - ], - [ - -0.6617734432220459, - 2.340578079223633, - 1.300318717956543 - ] - ], - "y": [ - [ - 0.9698716402053833, - -1.0028492212295532, - 1.991408348083496 - ], - [ - -2.099524974822998, - 1.2899032831192017, - 0.1857161521911621 - ] - ], - "z": [ - [ - 2.097663640975952, - -0.9279714226722717, - 0.18880197405815125 - ], - [ - -1.4660766124725342, - 1.2237895727157593, - -0.5910139679908752 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.9840936660766602, - -0.2602959871292114, - 0.8052687644958496 - ], - [ - -0.11981892585754395, - 1.8078397512435913, - 1.1989173889160156 - ] - ], - "y": [ - [ - 0.6130603551864624, - -0.7363213300704956, - 1.781500220298767 - ], - [ - -1.5439047813415527, - 0.8748706579208374, - 0.5125815868377686 - ] - ], - "z": [ - [ - 1.6833860874176025, - -0.6778335571289062, - 0.09814992547035217 - ], - [ - -0.8209702968597412, - 0.83427894115448, - -0.4498521685600281 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.7974835634231567, - -0.0768592357635498, - 0.8401840925216675 - ], - [ - -0.529433012008667, - 2.2104883193969727, - 1.275557518005371 - ] - ], - "y": [ - [ - 0.42174482345581055, - -0.5934139490127563, - 1.6689512729644775 - ], - [ - -1.9638476371765137, - 1.1885560750961304, - 0.2655336856842041 - ] - ], - "z": [ - [ - 1.4612579345703125, - -0.543714165687561, - 0.04954397678375244 - ], - [ - -1.3085477352142334, - 1.1286746263504028, - -0.5565435290336609 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.1453258991241455, - -0.4187866449356079, - 0.7751017212867737 - ], - [ - -0.541701078414917, - 2.222548007965088, - 1.2778528928756714 - ] - ], - "y": [ - [ - 0.7783583402633667, - -0.8597942590713501, - 1.8787431716918945 - ], - [ - -1.9764251708984375, - 1.1979511976242065, - 0.2581343650817871 - ] - ], - "z": [ - [ - 1.875306248664856, - -0.7937135696411133, - 0.14014580845832825 - ], - [ - -1.323150873184204, - 1.137492060661316, - -0.5597390532493591 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.5878195762634277, - 0.12923943996429443, - 0.8794128894805908 - ], - [ - 0.024000167846679688, - 1.6664661169052124, - 1.1720083951950073 - ] - ], - "y": [ - [ - 0.20679378509521484, - -0.4328516721725464, - 1.5424981117248535 - ], - [ - -1.3964591026306152, - 0.7647327184677124, - 0.5993220806121826 - ] - ], - "z": [ - [ - 1.2116880416870117, - -0.39302563667297363, - -0.005066812038421631 - ], - [ - -0.6497776508331299, - 0.7309139966964722, - -0.4123918414115906 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.3876922130584717, - 0.3259636163711548, - 0.9168573617935181 - ], - [ - 0.1833639144897461, - 1.5098124742507935, - 1.142190933227539 - ] - ], - "y": [ - [ - 0.0016200542449951172, - -0.27959275245666504, - 1.4217966794967651 - ], - [ - -1.233076810836792, - 0.6426910161972046, - 0.6954381465911865 - ] - ], - "z": [ - [ - 0.9734699726104736, - -0.2491912841796875, - -0.0571935772895813 - ], - [ - -0.4600818157196045, - 0.6163769960403442, - -0.37088268995285034 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.005493640899658, - -0.28133225440979004, - 0.801264762878418 - ], - [ - -0.21794629096984863, - 1.9042984247207642, - 1.2172772884368896 - ] - ], - "y": [ - [ - 0.6350002288818359, - -0.7527097463607788, - 1.7944071292877197 - ], - [ - -1.6445064544677734, - 0.9500173330307007, - 0.4533984661102295 - ] - ], - "z": [ - [ - 1.7088593244552612, - -0.6932141780853271, - 0.10372400283813477 - ], - [ - -0.9377748966217041, - 0.9048045873641968, - -0.47541123628616333 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.9046074151992798, - -0.18216156959533691, - 0.8201408982276917 - ], - [ - -0.19312572479248047, - 1.8799000978469849, - 1.2126333713531494 - ] - ], - "y": [ - [ - 0.5315699577331543, - -0.6754502058029175, - 1.7335602045059204 - ], - [ - -1.6190600395202637, - 0.9310096502304077, - 0.4683685302734375 - ] - ], - "z": [ - [ - 1.588771104812622, - -0.6207057237625122, - 0.0774463415145874 - ], - [ - -0.9082300662994385, - 0.8869656324386597, - -0.46894627809524536 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.059861421585083, - -0.334775447845459, - 0.7910923957824707 - ], - [ - -0.08346962928771973, - 1.7721086740493774, - 1.192116379737854 - ] - ], - "y": [ - [ - 0.6907387971878052, - -0.7943449020385742, - 1.827197551727295 - ], - [ - -1.506639003753662, - 0.8470340967178345, - 0.5345046520233154 - ] - ], - "z": [ - [ - 1.773574948310852, - -0.7322889566421509, - 0.11788502335548401 - ], - [ - -0.7777025699615479, - 0.8081542253494263, - -0.4403843283653259 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.2079925537109375, - -0.4803875684738159, - 0.7633765935897827 - ], - [ - -0.5202538967132568, - 2.201465606689453, - 1.273840069770813 - ] - ], - "y": [ - [ - 0.8426052331924438, - -0.9077848196029663, - 1.916538953781128 - ], - [ - -1.954437255859375, - 1.181526780128479, - 0.2710697650909424 - ] - ], - "z": [ - [ - 1.9499003887176514, - -0.8387529850006104, - 0.1564684808254242 - ], - [ - -1.297621488571167, - 1.1220775842666626, - -0.55415278673172 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.3355813026428223, - -0.605806827545166, - 0.739504337310791 - ], - [ - -0.6499292850494385, - 2.3289356231689453, - 1.2981027364730835 - ] - ], - "y": [ - [ - 0.9734114408493042, - -1.005493402481079, - 1.9934906959533691 - ], - [ - -2.0873823165893555, - 1.280833125114441, - 0.19285964965820312 - ] - ], - "z": [ - [ - 2.101773738861084, - -0.9304530024528503, - 0.18970128893852234 - ], - [ - -1.4519784450531006, - 1.215276837348938, - -0.5879289507865906 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.910269021987915, - -0.18772685527801514, - 0.8190816044807434 - ], - [ - -0.16418671607971191, - 1.8514529466629028, - 1.2072187662124634 - ] - ], - "y": [ - [ - 0.5373742580413818, - -0.6797858476638794, - 1.736974835395813 - ], - [ - -1.5893912315368652, - 0.9088476896286011, - 0.4858222007751465 - ] - ], - "z": [ - [ - 1.5955101251602173, - -0.6247748136520386, - 0.07892099022865295 - ], - [ - -0.8737828731536865, - 0.8661667108535767, - -0.46140849590301514 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.9682292938232422, - -0.24470150470733643, - 0.8082370758056641 - ], - [ - -0.35120391845703125, - 2.035289764404297, - 1.2422102689743042 - ] - ], - "y": [ - [ - 0.5967960357666016, - -0.7241722345352173, - 1.7719320058822632 - ], - [ - -1.7811241149902344, - 1.0520669221878052, - 0.3730278015136719 - ] - ], - "z": [ - [ - 1.6645021438598633, - -0.6664316654205322, - 0.09401777386665344 - ], - [ - -1.09639573097229, - 1.000578761100769, - -0.5101205706596375 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.5276515483856201, - 0.18838441371917725, - 0.8906705379486084 - ], - [ - 0.0162503719329834, - 1.6740843057632446, - 1.1734583377838135 - ] - ], - "y": [ - [ - 0.14510869979858398, - -0.38677453994750977, - 1.506209373474121 - ], - [ - -1.4044041633605957, - 0.7706676721572876, - 0.5946481227874756 - ] - ], - "z": [ - [ - 1.1400680541992188, - -0.34978199005126953, - -0.020738661289215088 - ], - [ - -0.6590025424957275, - 0.7364839315414429, - -0.41441041231155396 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.7626832723617554, - -0.042650818824768066, - 0.8466953635215759 - ], - [ - -0.07687139511108398, - 1.7656224966049194, - 1.1908817291259766 - ] - ], - "y": [ - [ - 0.3860670328140259, - -0.5667636394500732, - 1.6479624509811401 - ], - [ - -1.4998741149902344, - 0.841981053352356, - 0.5384843349456787 - ] - ], - "z": [ - [ - 1.4198341369628906, - -0.5187027454376221, - 0.0404796302318573 - ], - [ - -0.7698485851287842, - 0.8034118413925171, - -0.4386656880378723 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.6506389379501343, - 0.06748831272125244, - 0.8676592111587524 - ], - [ - -0.1518692970275879, - 1.839345097541809, - 1.2049140930175781 - ] - ], - "y": [ - [ - 0.2711973190307617, - -0.48095929622650146, - 1.5803859233856201 - ], - [ - -1.5767631530761719, - 0.8994148969650269, - 0.49325132369995117 - ] - ], - "z": [ - [ - 1.286463975906372, - -0.438174843788147, - 0.011295616626739502 - ], - [ - -0.8591210842132568, - 0.8573139905929565, - -0.45820021629333496 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.9798927307128906, - -0.25616657733917236, - 0.8060548305511475 - ], - [ - -0.24350476264953613, - 1.9294222593307495, - 1.2220594882965088 - ] - ], - "y": [ - [ - 0.6087535619735718, - -0.7331043481826782, - 1.7789665460586548 - ], - [ - -1.6707093715667725, - 0.9695900678634644, - 0.43798375129699707 - ] - ], - "z": [ - [ - 1.678385615348816, - -0.6748143434524536, - 0.09705573320388794 - ], - [ - -0.9681975841522217, - 0.9231737852096558, - -0.48206835985183716 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.837321400642395, - -0.11601972579956055, - 0.8327302932739258 - ], - [ - -0.5861842632293701, - 2.2662744522094727, - 1.2861758470535278 - ] - ], - "y": [ - [ - 0.46258723735809326, - -0.6239221096038818, - 1.6929785013198853 - ], - [ - -2.0220298767089844, - 1.232016682624817, - 0.23130559921264648 - ] - ], - "z": [ - [ - 1.5086781978607178, - -0.5723463296890259, - 0.05992045998573303 - ], - [ - -1.3761003017425537, - 1.1694625616073608, - -0.5713254809379578 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.3211731910705566, - 0.391351580619812, - 0.9293032884597778 - ], - [ - 0.18476390838623047, - 1.5084360837936401, - 1.1419289112091064 - ] - ], - "y": [ - [ - -0.06657648086547852, - -0.22865188121795654, - 1.3816773891448975 - ], - [ - -1.2316412925720215, - 0.6416186094284058, - 0.6962826251983643 - ] - ], - "z": [ - [ - 0.8942899703979492, - -0.20138299465179443, - -0.07451969385147095 - ], - [ - -0.45841526985168457, - 0.6153708696365356, - -0.3705180287361145 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.1164517402648926, - -0.3904033899307251, - 0.7805042266845703 - ], - [ - -0.5877268314361572, - 2.2677907943725586, - 1.2864644527435303 - ] - ], - "y": [ - [ - 0.7487560510635376, - -0.8376821279525757, - 1.861328363418579 - ], - [ - -2.023611545562744, - 1.2331980466842651, - 0.2303752899169922 - ] - ], - "z": [ - [ - 1.8409361839294434, - -0.7729612588882446, - 0.13262498378753662 - ], - [ - -1.3779370784759521, - 1.170571208000183, - -0.5717273354530334 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.849792242050171, - -0.12827837467193604, - 0.8303970098495483 - ], - [ - -0.10737419128417969, - 1.7956064939498901, - 1.1965889930725098 - ] - ], - "y": [ - [ - 0.47537243366241455, - -0.6334723234176636, - 1.7004998922348022 - ], - [ - -1.5311460494995117, - 0.8653403520584106, - 0.5200872421264648 - ] - ], - "z": [ - [ - 1.5235226154327393, - -0.5813091993331909, - 0.06316870450973511 - ], - [ - -0.8061568737030029, - 0.8253346681594849, - -0.446610689163208 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.6032534837722778, - 0.1140679121017456, - 0.8765251636505127 - ], - [ - 0.044921875, - 1.6459003686904907, - 1.1680939197540283 - ] - ], - "y": [ - [ - 0.22261691093444824, - -0.4446711540222168, - 1.5518066883087158 - ], - [ - -1.3750097751617432, - 0.7487107515335083, - 0.6119406223297119 - ] - ], - "z": [ - [ - 1.2300593852996826, - -0.4041181802749634, - -0.0010467767715454102 - ], - [ - -0.6248738765716553, - 0.7158774137496948, - -0.4069424271583557 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.7957884073257446, - -0.07519304752349854, - 0.8405013084411621 - ], - [ - -0.4216163158416748, - 2.1045050621032715, - 1.2553846836090088 - ] - ], - "y": [ - [ - 0.4200068712234497, - -0.5921158790588379, - 1.66792893409729 - ], - [ - -1.8533124923706055, - 1.1059893369674683, - 0.33056044578552246 - ] - ], - "z": [ - [ - 1.459240198135376, - -0.5424959659576416, - 0.049102455377578735 - ], - [ - -1.1802098751068115, - 1.0511852502822876, - -0.5284608006477356 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 1.8687241077423096, - -0.1468883752822876, - 0.8268548250198364 - ], - [ - -0.5498387813568115, - 2.2305469512939453, - 1.2793755531311035 - ] - ], - "y": [ - [ - 0.4947817325592041, - -0.64797043800354, - 1.7119181156158447 - ], - [ - -1.9847679138183594, - 1.2041829824447632, - 0.25322651863098145 - ] - ], - "z": [ - [ - 1.546057939529419, - -0.5949157476425171, - 0.06809985637664795 - ], - [ - -1.3328373432159424, - 1.1433404684066772, - -0.561858594417572 - ] - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - [ - 2.0208489894866943, - -0.2964264154434204, - 0.7983917593955994 - ], - [ - -0.22210168838500977, - 1.9083832502365112, - 1.2180547714233398 - ] - ], - "y": [ - [ - 0.6507426500320435, - -0.7644689083099365, - 1.8036682605743408 - ], - [ - -1.6487667560577393, - 0.9531995058059692, - 0.45089244842529297 - ] - ], - "z": [ - [ - 1.7271372079849243, - -0.7042502164840698, - 0.10772353410720825 - ], - [ - -0.9427211284637451, - 0.907791018486023, - -0.4764935374259949 - ] - ] - } - ], - "layout": { - "autosize": false, - "height": 1000, - "margin": { - "b": 100, - "l": 50, - "pad": 4, - "r": 50, - "t": 100 - }, - "scene": { - "aspectmode": "data", - "aspectratio": { - "x": 1.0846410230825063, - "y": 1.114784688198523, - "z": 0.8270332663321689 - }, - "camera": { - "center": { - "x": 0, - "y": 0, - "z": 0 - }, - "eye": { - "x": 1.7540937848022615, - "y": 0.9281605147086744, - "z": 0.8655478340645341 - }, - "projection": { - "type": "perspective" - }, - "up": { - "x": 0, - "y": 0, - "z": 1 - } - } - }, - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - }, - "width": 1000 - } - }, - "image/png": "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AQQQQAABBBBAAAEEEEAAAQQQQAABQlXmAAIIIIAAAggggAACCCCAAAIIIIAAAgggYCFAqGqBxaYIIIAAAggggAACCCCAAAIIIIAAAggggAChKnMAAQQQQAABBBBAAAEEEEAAAQQQQAABBBCwECBUtcBiUwQQQAABBBBAAAEEEEAAAQQQQAABBBBAgFCVOYAAAggggAACCCCAAAIIIIAAAggggAACCFgIEKpaYLEpAggggAACCCCAAAIIIIAAAggggAACCCBAqMocQAABBBBAAAEEEEAAAQQQQAABBBBAAAEELAQIVS2w2BQBBBBAAAEEEEAAAQQQQAABBBBAAAEEECBUZQ4ggAACCCCAAAIIIIAAAggggAACCCCAAAIWAoSqFlhsigACCCCAAAIIIIAAAggggAACCCCAAAIIEKoyBxBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQsBQlULLDZFAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQIVZkDCCCAAAIIIIAAAggggAACCCCAAAIIIICAhQChqgUWmyKAAAIIIIAAAggggAACCCCAAAIIIIAAAoSqzAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQMBCgFDVAotNEUAAAQQQQAABBBBAAAEEEEAAAQQQQAABQlXmAAIIIIAAAggggAACCCCAAAIIIIAAAgggYCFAqGqBxaYIIIAAAggggAACCCCAAAIIIIAAAggggAChKnMAAQQQQAABBBBAAAEEEEAAAQQQQAABBBCwECBUtcBiUwQQQAABBBBAAAEEEEAAAQQQQAABBBBAgFCVOYAAAggggAACCCCAAAIIIIAAAggggAACCFgIEKpaYLEpAggggAACCCCAAAIIIIAAAggggAACCCBAqMocQAABBBBAAAEEEEAAAQQQQAABBBBAAAEELAQIVS2w2BQBBBBAAAEEEEAAAQQQQAABBBBAAAEE8hXo9wem0WyYdqsVu+OjXt902m3TbDZit3W5AaGqS132jQACCCCAAAIIIIAAAggggAACCCCAgGcC58+fN+985zvNrbfeatrttrn++uvNU57ylNLO8lkveKW54SEPNC/70e9beQ69/sA88knPM7/2yh8333Ljw0s7Xx2YULVUfg6OAAIIIIAAAggggAACCCCAAAIIIIBAcQIf/vCHzU033WTuuOOOiw76xCc+0dxy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IECAAAECCyLweL81M8HqxupyrDaW4+FuM7q9+QtUB0su1eb9wdvvxN/9r/+2IKvQMAkQIECAAAECBE4LCFWtBwIECBAgQIAAAQIECBAgQIAAAQIECGQICFUzsDQlQIAAAQIECBAgQIAAAQIECBAgQICAUNUaIECAAAECBAgQIECAAAECBAgQIECAQIaAUDUDS1MCBAgQIECAAAECBAgQIECAAAECBAgIVa0BAgQIECBAgAABAgQIECBAgAABAgQIZAgIVTOwNCVAgAABAgQIECBAgAABAgQIECBAgIBQ1RogQIAAAQIECBAgQIAAAQIECBAgQIBAhoBQNQNLUwIECBAgQIAAAQIECBAgQIAAAQIECAhVrQECBAgQIECAAAECBAgQIECAAAECBAhkCAhVM7A0JUCAAAECBAgQIECAAAECBAgQIECAgFDVGiBAgAABAgQIECBAgAABAgQIECBAgECGgFA1A0tTAgQIECBAgAABAgQIECBAgAABAgQICFWtAQIECBAgQIAAAQIECBAgQIAAAQIECGQICFUzsDQlQIAAAQIECBAgQIAAAQIECBAgQICAUNUaIECAAAECBAgQIECAAAECBAgQIECAQIaAUDUDS1MCBAgQIECAAAECBAgQIECAAAECBAgIVa0BAgQIECBAgAABAgQIECBAgAABAgQIZAgIVTOwNCVAgAABAgQIECBAgAABAgQIECBAgIBQ1RogQIAAAQIECBAgQIAAAQIECBAgQIBAhoBQNQNLUwIECBAgQIAAAQIECBAgQIAAAQIECAhVrQECBAgQIECAAAECBAgQIECAAAECBAhkCAhVM7A0JUCAAAECBAgQIECAAAECBAgQIECAgFDVGiBAgAABAgQIECBAgAABAgQIECBAgECGgFA1A0tTAgQIECBAgAABAgQIECBAgAABAgQICFWtAQIECBAgQIAAAQIECBAgQIAAAQIECGQICFUzsDQlQIAAAQIECBAgQIAAAQIECBAgQICAUNUaIECAAAECBAgQIECAAAECBAgQIECAQIaAUDUDS1MCBAgQIECAAAECBAgQIECAAAFByfMuAAAHj0lEQVQCBAgIVa0BAgQIECBAgAABAgQIECBAgAABAgQIZAgIVTOwNCVAgAABAgQIECBAgAABAgQIECBAgIBQ1RogQIAAAQIECBAgQIAAAQIECBAgQIBAhoBQNQNLUwIECBAgQIAAAQIECBAgQIAAAQIECAhVrQECBAgQIECAAAECBAgQIECAAAECBAhkCAhVM7A0JUCAAAECBAgQIECAAAECBAgQIECAgFDVGiBAgAABAgQIECBAgAABAgQIECBAgECGgFA1A0tTAgQIECBAgAABAgQIECBAgAABAgQICFWtAQIECBAgQIAAAQIECBAgQIAAAQIECGQICFUzsDQlQIAAAQIECBAgQIAAAQIECBAgQICAUNUaIECAAAECBAgQIECAAAECBAgQIECAQIaAUDUDS1MCBAgQIECAAAECBAgQIECAAAECBAgIVa0BAgQIECBAgAABAgQIECBAgAABAgQIZAgIVTOwNCVAgAABAgQIECBAgAABAgQIECBAgIBQ1RogQIAAAQIECBAgQIAAAQIECBAgQIBAhoBQNQNLUwIECBAgQIAAAQIECBAgQIAAAQIECAhVrQECBAgQIECAAAECBAgQIECAAAECBAhkCAhVM7A0JUCAAAECBAgQIECAAAECBAgQIECAgFDVGiBAgAABAgQIECBAgAABAgQIECBAgECGgFA1A0tTAgQIECBAgAABAgQIECBAgAABAgQICFWtAQIECBAgQIAAAQIECBAgQIAAAQIECGQICFUzsDQlQIAAAQIECBAgQIAAAQIECBAgQICAUNUaIECAAAECBAgQIECAAAECBAgQIECAQIaAUDUDS1MCBAgQIECAAAECBAgQIECAAAECBAgIVa0BAgQIECBAgAABAgQIECBAgAABAgQIZAgIVTOwNCVAgAABAgQIECBAgAABAgQIECBAgIBQ1RogQIAAAQIECBAgQIAAAQIECBAgQIBAhoBQNQNLUwIECBAgQIAAAQIECBAgQIAAAQIECAhVrQECBAgQIECAAAECBAgQIECAAAECBAhkCAhVM7A0JUCAAAECBAgQIECAAAECBAgQIECAgFDVGiBAgAABAgQIECBAgAABAgQIECBAgECGgFA1A0tTAgQIECBAgAABAgQIECBAgAABAgQICFWtAQIECBAgQIAAAQIECBAgQIAAAQIECGQICFUzsDQlQIAAAQIECBAgQIAAAQIECBAgQICAUNUaIECAAAECBAgQIECAAAECBAgQIECAQIaAUDUDS1MCBAgQIECAAAECBAgQIECAAAECBAgIVa0BAgQIECBAgAABAgQIECBAgAABAgQIZAgIVTOwNCVAgAABAgQIECBAgAABAgQIECBAgIBQ1RogQIAAAQIECBAgQIAAAQIECBAgQIBAhoBQNQNLUwIECBAgQIAAAQIECBAgQIAAAQIECAhVrQECBAgQIECAAAECBAgQIECAAAECBAhkCAhVM7A0JUCAAAECBAgQIECAAAECBAgQIECAgFDVGiBAgAABAgQIECBAgAABAgQIECBAgECGgFA1A0tTAgQIECBAgAABAgQIECBAgAABAgQICFWtAQIECBAgQIAAAQIECBAgQIAAAQIECGQICFUzsDQlQIAAAQIECBAgQIAAAQIECBAgQICAUNUaIECAAAECBAgQIECAAAECBAgQIECAQIaAUDUDS1MCBAgQIECAAAECBAgQIECAAAECBAgIVa0BAgQIECBAgAABAgQIECBAgAABAgQIZAgIVTOwNCVAgAABAgQIECBAgAABAgQIECBAgIBQ1RogQIAAAQIECBAgQIAAAQIECBAgQIBAhoBQNQNLUwIECBAgQIAAAQIECBAgQIAAAQIECAhVrQECBAgQIECAAAECBAgQIECAAAECBAhkCAhVM7A0JUCAAAECBAgQIECAAAECBAgQIECAgFDVGiBAgAABAgQIECBAgAABAgQIECBAgECGgFA1A0tTAgQIECBAgAABAgQIECBAgAABAgQICFWtAQIECBAgQIAAAQIECBAgQIAAAQIECGQICFUzsDQlQIAAAQIECBAgQIAAAQIECBAgQICAUNUaIECAAAECBAgQIECAAAECBAgQIECAQIaAUDUDS1MCBAgQIECAAAECBAgQIECAAAECBAgIVa0BAgQIECBAgAABAgQIECBAgAABAgQIZAgIVTOwNCVAgAABAgQIECBAgAABAgQIECBAgIBQ1RogQIAAAQIECBAgQIAAAQIECBAgQIBAhoBQNQNLUwIECBAgQIAAAQIECBAgQIAAAQIECAhVrQECBAgQIECAAAECBAgQIECAAAECBAhkCAhVM7A0JUCAAAECBAgQIECAAAECBAgQIECAgFDVGiBAgAABAgQIECBAgAABAgQIECBAgECGgFA1A0tTAgQIECBAgAABAgQIECBAgAABAgQICFWtAQIECBAgQIAAAQIECBAgQIAAAQIECGQICFUzsDQlQIAAAQIECBAgQIAAAQIECBAgQICAUNUaIECAAAECBAgQIECAAAECBAgQIECAQIaAUDUDS1MCBAgQIECAAAECBAgQIECAAAECBAj8M2oDxIIiJuyVAAAAAElFTkSuQmCC", - "text/html": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ray_indices = batch[\"indices\"]\n", - "ray_bundle = ray_generator(ray_indices)\n", - "\n", - "skip = 1\n", - "size = 8\n", - "data = []\n", - "data += [\n", - " go.Scatter3d(\n", - " x=ray_generator.camera_to_world[::skip, 0, 3],\n", - " y=ray_generator.camera_to_world[::skip, 1, 3],\n", - " z=ray_generator.camera_to_world[::skip, 2, 3],\n", - " mode=\"markers\",\n", - " name=\"origins\",\n", - " marker=dict(color=\"rgba(0, 0, 0, 1)\", size=size),\n", - " )\n", - "]\n", - "\n", - "scene_bounds_collider = AABBBoxCollider(dataset_inputs.scene_bounds)\n", - "intersected_ray_bundle = scene_bounds_collider(ray_bundle)\n", - "\n", - "lines = torch.stack(\n", - " [\n", - " intersected_ray_bundle.origins + intersected_ray_bundle.directions * intersected_ray_bundle.nears[..., None],\n", - " intersected_ray_bundle.origins + intersected_ray_bundle.directions * intersected_ray_bundle.fars[..., None],\n", - " ],\n", - " dim=1,\n", - ").tolist() # (num_rays, 2, 3)\n", - "lines = torch.tensor(random.sample(lines, k=100))\n", - "\n", - "data += get_line_segments_from_lines(lines)\n", - "\n", - "layout = go.Layout(\n", - " autosize=False,\n", - " width=1000,\n", - " height=1000,\n", - " margin=go.layout.Margin(l=50, r=50, b=100, t=100, pad=4),\n", - " scene=go.layout.Scene(\n", - " aspectmode=\"data\",\n", - " camera=dict(up=dict(x=0, y=0, z=1), center=dict(x=0, y=0, z=0), eye=dict(x=1.25, y=1.25, z=1.25)),\n", - " ),\n", - ")\n", - "fig = go.Figure(data=data, layout=layout)\n", - "fig.show()" - ] - } - ], - "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.8.13" - }, - "vscode": { - "interpreter": { - "hash": "41f62990c6e03ccb48eec29860b83c64e20fcd45204ae5014afab6809cd6f1f1" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/visualize_viewer.ipynb b/notebooks/visualize_viewer.ipynb deleted file mode 100644 index 3c686f9af..000000000 --- a/notebooks/visualize_viewer.ipynb +++ /dev/null @@ -1,129 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "3c00934d-0f14-4c1a-a563-94d627b377e5", - "metadata": {}, - "source": [ - "## Notebook to visualize the viewer functionality\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b15dfdc1-5054-40bc-9631-4c7426eecfd9", - "metadata": {}, - "outputs": [], - "source": [ - "from notebook_imports import *\n", - "\n", - "import time\n", - "import torch\n", - "from torch.utils.data import DataLoader\n", - "\n", - "from nerfactory.data.dataloader import TrainDataloader\n", - "from nerfactory.data.image_dataset import ImageDataset, PanopticImageDataset\n", - "from nerfactory.data.image_sampler import CacheImageSampler\n", - "from nerfactory.data.pixel_sampler import PixelSampler\n", - "from nerfactory.data.utils import DatasetInputs, get_dataset_inputs_from_dataset_config\n", - "from nerfactory.graphs.modules.ray_generator import RayGenerator\n", - "from nerfactory.graphs.modules.scene_colliders import SceneBoundsCollider, AABBBoxCollider\n", - "from nerfactory.cameras.cameras import get_camera\n", - "from nerfactory.cameras.rays import RayBundle\n", - "from nerfactory.utils.io import get_absolute_path\n", - "from nerfactory.utils.plotly import get_line_segments_from_lines\n", - "from nerfactory.cameras.cameras import get_camera_model\n", - "from nerfactory.utils.misc import get_dict_to_torch, instantiate_from_dict_config\n", - "from nerfactory.engine.trainer import Trainer\n", - "from nerfactory.utils.config import Config, setup_config\n", - "\n", - "from hydra import compose, initialize\n", - "from omegaconf import open_dict\n", - "import pprint\n", - "from tqdm import tqdm\n", - "import random\n", - "\n", - "import numpy as np\n", - "\n", - "import umsgpack\n", - "from nerfactory.viewer.server.utils import get_chunks, get_intrinsics_matrix_and_camera_to_world_h\n", - "\n", - "from nerfactory.viewer.server import viewer_utils\n", - "from nerfactory.viewer.server.visualizer import Viewer\n", - "from nerfactory.viewer.server.utils import get_camera_from_vis" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "26d6da3c-e28a-4c55-9592-d90dccb86051", - "metadata": {}, - "outputs": [], - "source": [ - "# connect to the viewer\n", - "vis = Viewer(zmq_url=\"tcp://127.0.0.1:6000\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "605e0131-bfbe-4309-b338-c49a46923d8f", - "metadata": {}, - "outputs": [], - "source": [ - "# draw a red box\n", - "viewer_utils.show_box_test(vis)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1b3ba0e1-ade8-4bda-bcb4-0dd2ca74dda4", - "metadata": {}, - "outputs": [], - "source": [ - "# move the camera in a spiral path\n", - "camera = get_camera_from_vis(vis)\n", - "camera_path = get_spiral_path(camera, steps=120, radius=0.5)\n", - "seconds = 5\n", - "fps = len(camera_path.cameras) / seconds\n", - "spf = 1 / fps\n", - "for cam in camera_path.cameras:\n", - " viewer_utils.set_camera(vis, cam)\n", - " time.sleep(spf)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5a01cf1c-07d1-47fd-a39b-817298a83760", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "interpreter": { - "hash": "4a749acc7d255f078aee52e0584cc77b3cb5aaed1b3a7407ec4262c1bf6cb526" - }, - "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.8.13" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/package.json b/package.json deleted file mode 100644 index 5c8077a13..000000000 --- a/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "dependencies": {}, - "devDependencies": { - "prettier": "2.7.1" - } -} diff --git a/pyproject.toml b/pyproject.toml index 6b63cf77b..6109b948c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ line-length = 120 max-line-length = 120 generated-members = ["numpy.*", "torch.*", "cv2.*", "cv.*"] good-names-rgxs = "^[_a-zA-Z][_a-z0-9]?$" -ignore-paths = ["nerfactory/ngp_pl/.*$", "scripts/colmap2nerf.py", "^nerfactory/cuda/.*$", "^tests/cuda/.*$", "^nerfactory/viewer/.*$", "^tests/viewer/.*$"] +ignore-paths = ["scripts/colmap2nerf.py", "^nerfactory/cuda/.*$", "^tests/cuda/.*$"] disable = [ "too-many-arguments", @@ -40,7 +40,7 @@ disable = [ #pytest [tool.pytest.ini_options] -addopts = "--typeguard-packages=nerfactory --torchtyping-patch-typeguard --disable-warnings --ignore=tests/cuda --ignore=tests/viewer" +addopts = "--typeguard-packages=nerfactory --torchtyping-patch-typeguard --disable-warnings --ignore=tests/cuda" testpaths = [ "tests", ] diff --git a/scripts/debugging/run_actions.py b/scripts/debugging/run_actions.py index bfd9130e0..9827e1953 100644 --- a/scripts/debugging/run_actions.py +++ b/scripts/debugging/run_actions.py @@ -26,7 +26,7 @@ def main(continue_on_fail: bool = False): """Run the github actions locally. Args: - continue_on_fail: Whether to continue running actions commands if the current one fails + continue_on_fail: Whether or not to continue running actions commands if the current one fails """ with open(".github/workflows/code_checks.yml", "rb") as f: my_dict = yaml.safe_load(f) diff --git a/tests/viewer/test_viewer.py b/tests/viewer/test_viewer.py deleted file mode 100644 index 5a3c31300..000000000 --- a/tests/viewer/test_viewer.py +++ /dev/null @@ -1,192 +0,0 @@ -""" -Test the viewer comms code. -""" - - -import random -import time - -import cv2 -import numpy as np -import umsgpack -from tqdm import tqdm - -import nerfactory.viewer.server.geometry as g -from nerfactory.cameras import camera_paths -from nerfactory.cameras.cameras import get_camera -from nerfactory.data.datasets import Blender -from nerfactory.utils.io import get_absolute_path -from nerfactory.viewer.server.transformations import ( - get_rotation_matrix, - get_translation_matrix, -) -from nerfactory.viewer.server.viewer_utils import get_default_vis, set_camera - - -def test_drawing(): - """Test drawing objects in the viewer.""" - vis = get_default_vis() - vis.delete() - - v = vis["shapes"] - v.set_transform(get_translation_matrix([1.0, 0, 0])) - v["box"].set_object(g.Box([1.0, 0.2, 0.3])) - v["box"].delete() - v["box"].set_object(g.Box([0.1, 0.2, 0.3])) - v["box"].set_transform(get_translation_matrix([0.05, 0.1, 0.15])) - v["cylinder"].set_object(g.Cylinder(0.2, 0.1), g.MeshLambertMaterial(color=0x22DD22)) - v["cylinder"].set_transform(get_translation_matrix([0, 0.5, 0.1]).dot(get_rotation_matrix(-np.pi / 2, [1, 0, 0]))) - v["sphere"].set_object(g.Mesh(g.Sphere(0.15), g.MeshLambertMaterial(color=0xFF11DD))) - v["sphere"].set_transform(get_translation_matrix([0, 1, 0.15])) - v["ellipsoid"].set_object(g.Ellipsoid([0.3, 0.1, 0.1])) - v["ellipsoid"].set_transform(get_translation_matrix([0, 1.5, 0.1])) - - v["transparent_ellipsoid"].set_object( - g.Mesh(g.Ellipsoid([0.3, 0.1, 0.1]), g.MeshLambertMaterial(color=0xFFFFFF, opacity=0.5)) - ) - v["transparent_ellipsoid"].set_transform(get_translation_matrix([0, 2.0, 0.1])) - - v = vis["meshes/convex"] - v["obj"].set_object( - g.Mesh(g.ObjMeshGeometry.from_file(get_absolute_path("tests/viewer/data/mesh_0_convex_piece_0.obj"))) - ) - v["stl_ascii"].set_object( - g.Mesh(g.StlMeshGeometry.from_file(get_absolute_path("tests/viewer/data/mesh_0_convex_piece_0.stl_ascii"))) - ) - v["stl_ascii"].set_transform(get_translation_matrix([0, -0.5, 0])) - v["stl_binary"].set_object( - g.Mesh(g.StlMeshGeometry.from_file(get_absolute_path("tests/viewer/data/mesh_0_convex_piece_0.stl_binary"))) - ) - v["stl_binary"].set_transform(get_translation_matrix([0, -1, 0])) - v["dae"].set_object( - g.Mesh(g.DaeMeshGeometry.from_file(get_absolute_path("tests/viewer/data/mesh_0_convex_piece_0.dae"))) - ) - v["dae"].set_transform(get_translation_matrix([0, -1.5, 0])) - - v = vis["points"] - v.set_transform(get_translation_matrix([0, 2, 0])) - verts = np.random.rand(3, 1000000) - colors = verts - v["random"].set_object(g.PointCloud(verts, colors)) - v["random"].set_transform(get_translation_matrix([-0.5, -0.5, 0])) - - v = vis["lines"] - v.set_transform(get_translation_matrix([-2, -3, 0])) - - vertices = np.random.random((3, 10)).astype(np.float32) - v["line_segments"].set_object(g.LineSegments(g.PointsGeometry(vertices))) - - v["line"].set_object(g.Line(g.PointsGeometry(vertices))) - v["line"].set_transform(get_translation_matrix([0, 1, 0])) - - v["line_loop"].set_object(g.LineLoop(g.PointsGeometry(vertices))) - v["line_loop"].set_transform(get_translation_matrix([0, 2, 0])) - - v["line_loop_with_material"].set_object(g.LineLoop(g.PointsGeometry(vertices), g.LineBasicMaterial(color=0xFF0000))) - v["line_loop_with_material"].set_transform(get_translation_matrix([0, 3, 0])) - - colors = vertices # Color each line by treating its xyz coordinates as RGB colors - v["line_with_vertex_colors"].set_object( - g.Line(g.PointsGeometry(vertices, colors), g.LineBasicMaterial(vertexColors=True)) - ) - v["line_with_vertex_colors"].set_transform(get_translation_matrix([0, 4, 0])) - - v["triad"].set_object( - g.LineSegments( - g.PointsGeometry( - position=np.array([[0, 0, 0], [1, 0, 0], [0, 0, 0], [0, 1, 0], [0, 0, 0], [0, 0, 1]]) - .astype(np.float32) - .T, - color=np.array([[1, 0, 0], [1, 0.6, 0], [0, 1, 0], [0.6, 1, 0], [0, 0, 1], [0, 0.6, 1]]) - .astype(np.float32) - .T, - ), - g.LineBasicMaterial(vertexColors=True), - ) - ) - v["triad"].set_transform(get_translation_matrix([0, 5, 0])) - - v["triad_function"].set_object(g.triad(0.5)) - v["triad_function"].set_transform(get_translation_matrix([0, 6, 0])) - - -def test_streaming(): - """Test streaming of data to the viewer.""" - - -def test_rendering(): - """Test rendering.""" - - -def test_camera_trajectory(): - """Test generating camera trajectory.""" - vis = get_default_vis() - # vis.delete() - - # sample poses from a dataset - dataset = Blender(data_directory="data/blender/lego") - dataset_inputs = dataset.get_dataset_inputs(split="train") - num_cameras = len(dataset_inputs.intrinsics) - intrinsics = dataset_inputs.intrinsics - camera_to_world = dataset_inputs.camera_to_world - idx0, idx1 = random.sample(range(num_cameras), k=2) - camera_a = get_camera(intrinsics[idx0], camera_to_world[idx0], None) - camera_b = get_camera(intrinsics[idx1], camera_to_world[idx1], None) - - num_steps = 100 - fps = 30 - estimated_time = num_steps / fps - print("estimated_time:", estimated_time) - - camera_path = camera_paths.get_interpolated_camera_path(camera_a, camera_b, steps=num_steps) - - start = time.time() - for camera in camera_path.cameras: - set_camera(vis, camera) - time.sleep(1 / fps) - time_elapsed = time.time() - start - print("time_elapsed:", time_elapsed) - - -def test_send_image_stream(): - """Test sending image stream.""" - vis = get_default_vis() - vis_background = vis["/Background"] - - video_filename = get_absolute_path("data/instant_ngp/bear/bear.MOV") - images = [] - cap = cv2.VideoCapture(video_filename) - ret, image = cap.read() - while ret: - image = image[:, :, ::-1] # to RGB - image = cv2.resize(image, (200, 300)) - images.append(image) - ret, image = cap.read() - # break - - fps = 30 - for image in tqdm(images): - rgba = cv2.cvtColor(image, cv2.COLOR_RGB2RGBA) - encoded = umsgpack.Ext(0x12, rgba.tobytes()) - vis_background.set_image(encoded) - time.sleep(1 / fps) - - -def test_perspective_camera(): - """Test perspective camera in viewer.""" - vis = get_default_vis() - vis.set_object(g.Box([0.5, 0.5, 0.5])) - camera = g.PerspectiveCamera(fov=90) - vis["/Cameras/default/rotated"].set_object(camera) - vis["/Cameras/default"].set_transform(get_translation_matrix([1, -1, 0.5])) - vis["/Cameras/default/rotated/"].set_property("position", [0, 0, 0]) - - -if __name__ == "__main__": - # TODO: start the TCP server - test_drawing() - time.sleep(5) - test_camera_trajectory() - test_send_image_stream() - # test_perspective_camera() - # TODO: close the TCP server